merge from donut

This commit is contained in:
Jean-Baptiste Queru
2009-07-29 14:56:49 -07:00
83 changed files with 6250 additions and 870 deletions

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Reserve"</string>
<string name="title">"Ikke understøttet handling"</string>
<string name="error">"Handlingen er ikke understøttet i øjeblikket."</string>
</resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Εναλλακτική"</string>
<string name="title">"Ενέργεια που δεν υποστηρίζεται"</string>
<string name="error">"Αυτή η ενέργεια δεν υποστηρίζεται αυτήν τη στιγμή."</string>
</resources>

View File

@@ -15,7 +15,7 @@
--> -->
<resources xmlns:android="http://schemas.android.com/apk/res/android" <resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Fallback"</string> <string name="appTitle">"폴백"</string>
<string name="title">"지원되지 않는 작업"</string> <string name="title">"지원되지 않는 작업"</string>
<string name="error">"이 작업은 현재 지원되지 않습니다."</string> <string name="error">"이 작업은 현재 지원되지 않습니다."</string>
</resources> </resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Fallback"</string>
<string name="title">"Acção não suportada"</string>
<string name="error">"Esta·acção·ainda·não·é·suportada."</string>
</resources>

View File

@@ -17,5 +17,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Fallback"</string> <string name="appTitle">"Fallback"</string>
<string name="title">"Ação não suportada"</string> <string name="title">"Ação não suportada"</string>
<string name="error">"Esta ação não é suportada no momento."</string> <string name="error">"Essa ação não é suportada no momento."</string>
</resources> </resources>

View File

@@ -15,7 +15,7 @@
--> -->
<resources xmlns:android="http://schemas.android.com/apk/res/android" <resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Fallback"</string> <string name="appTitle">"Переход в обходной режим"</string>
<string name="title">"Действие не поддерживается"</string> <string name="title">"Неподдерживаемое действие"</string>
<string name="error">"В настоящее время это действие не поддерживается."</string> <string name="error">"В настоящее время это действие не поддерживается."</string>
</resources> </resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Reserv"</string>
<string name="title">"Åtgärden stöds inte"</string>
<string name="error">"Den här åtgärden stöds inte för närvarande."</string>
</resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Fallback"</string>
<string name="title">"Desteklenmeyen işlem"</string>
<string name="error">"Bu işlem şu an desteklenmiyor."</string>
</resources>

View File

@@ -15,7 +15,7 @@
--> -->
<resources xmlns:android="http://schemas.android.com/apk/res/android" <resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Fallback"</string> <string name="appTitle">"后备"</string>
<string name="title">"不支持操作"</string> <string name="title">"不支持操作"</string>
<string name="error">"前不支持该操作。"</string> <string name="error">"前不支持该操作。"</string>
</resources> </resources>

View File

@@ -0,0 +1,25 @@
#
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
#
# 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.
#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := GestureBuilder
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.gesture.builder">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application>
<activity
android:name="GestureBuilderActivity"
android:label="@string/application_name"
android:icon="@drawable/ic_gesturebuilder">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name="CreateGestureActivity"
android:label="@string/label_create_gesture" />
</application>
</manifest>

190
apps/GestureBuilder/NOTICE Normal file
View File

@@ -0,0 +1,190 @@
Copyright (c) 2005-2008, The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) 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. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="6dip"
android:text="@string/prompt_gesture_name"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
android:id="@+id/gesture_name"
android:layout_width="0dip"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:maxLength="40"
android:singleLine="true" />
</LinearLayout>
<android.gesture.GestureOverlayView
android:id="@+id/gestures_overlay"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:gestureStrokeType="multiple" />
<LinearLayout
style="@android:style/ButtonBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/done"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:enabled="false"
android:onClick="addGesture"
android:text="@string/button_done" />
<Button
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="cancelGesture"
android:text="@string/button_discard" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="20dip"
android:orientation="vertical">
<TextView
android:id="@+id/label"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/gestures_rename_label"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
android:id="@+id/name"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:scrollHorizontally="true"
android:autoText="false"
android:capitalize="none"
android:gravity="fill_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:drawablePadding="12dip"
android:paddingLeft="6dip"
android:paddingRight="6dip"
android:ellipsize="marquee"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge" />

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0" />
<TextView
android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:gravity="center"
android:text="@string/gestures_loading"
android:textAppearance="?android:attr/textAppearanceMedium" />
<LinearLayout
style="@android:style/ButtonBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/addButton"
android:onClick="addGesture"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:enabled="false"
android:text="@string/button_add" />
<Button
android:id="@+id/reloadButton"
android:onClick="reloadGestures"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:enabled="false"
android:text="@string/button_reload" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/assets/res/any/colors.xml
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
**
** 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.
*/
-->
<resources>
<color name="gesture_color">#FFFFFF00</color>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<resources>
<dimen name="gesture_thumbnail_inset">8dip</dimen>
<dimen name="gesture_thumbnail_size">64dip</dimen>
</resources>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- General -->
<skip />
<!-- Application name -->
<string name="application_name">Gestures Builder</string>
<!-- Title, name of the activity used to create a gesture -->
<string name="label_create_gesture">Create a gesture</string>
<!-- Buttons -->
<skip />
<!-- Label, button used to add a gesture -->
<string name="button_add">Add gesture</string>
<!-- Label, button used to reload all gestures -->
<string name="button_reload">Reload</string>
<!-- Label, button used to cancel the operation of adding a gesture -->
<string name="button_discard">Discard</string>
<!-- Label, button used to save a gesture newly created -->
<string name="button_done">Done</string>
<!-- Gestures -->
<skip />
<!-- Label, prompt asking the user to enter the name of the gesture -->
<string name="prompt_gesture_name">Name</string>
<!-- Error message, informs the user he needs to enter a name before saving a gesture -->
<string name="error_missing_name">You must enter a name</string>
<!-- success message, tells the user where the gesture was saved -->
<string name="save_success">Gesture saved in %s</string>
<!-- Buttons in Rename gesture dialog box -->
<string name="rename_action">OK</string>
<!-- Buttons in Rename gesture dialog box -->
<string name="cancel_action">Cancel</string>
<!-- Message displayed when the user opens the gestures settings screen -->
<string name="gestures_loading">Loading gestures...</string>
<!-- Message displayed when the user has no gestures -->
<string name="gestures_empty">No gestures</string>
<!-- Title of the screen used to view/manage gestures -->
<string name="gestures_activity">Gestures</string>
<!-- Noun, menu item used to rename a gesture -->
<string name="gestures_rename">Rename</string>
<!-- Noun, menu item used to remove a gesture -->
<string name="gestures_delete">Delete</string>
<!-- Message displayed when a gesture is successfully deleted -->
<string name="gestures_delete_success">Gesture deleted</string>
<!-- Title of dialog box -->
<string name="gestures_rename_title">Rename gesture</string>
<!-- Label of gesture name field in Rename gesture dialog box -->
<string name="gestures_rename_label">Gesture name</string>
<!-- Message, displayed when the sdcard cannot be found, 1st parameter is the name of the file that stores the gestures -->
<string name="gestures_error_loading">Could not load %s. Make sure you have a mounted SD card.</string>
</resources>

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.gesture.builder;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.MotionEvent;
import android.gesture.GestureOverlayView;
import android.gesture.Gesture;
import android.gesture.GestureLibrary;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
public class CreateGestureActivity extends Activity {
private static final float LENGTH_THRESHOLD = 120.0f;
private Gesture mGesture;
private View mDoneButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_gesture);
mDoneButton = findViewById(R.id.done);
GestureOverlayView overlay = (GestureOverlayView) findViewById(R.id.gestures_overlay);
overlay.addOnGestureListener(new GesturesProcessor());
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mGesture != null) {
outState.putParcelable("gesture", mGesture);
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mGesture = savedInstanceState.getParcelable("gesture");
if (mGesture != null) {
final GestureOverlayView overlay =
(GestureOverlayView) findViewById(R.id.gestures_overlay);
overlay.post(new Runnable() {
public void run() {
overlay.setGesture(mGesture);
}
});
mDoneButton.setEnabled(true);
}
}
@SuppressWarnings({"UnusedDeclaration"})
public void addGesture(View v) {
if (mGesture != null) {
final TextView input = (TextView) findViewById(R.id.gesture_name);
final CharSequence name = input.getText();
if (name.length() == 0) {
input.setError(getString(R.string.error_missing_name));
return;
}
final GestureLibrary store = GestureBuilderActivity.getStore();
store.addGesture(name.toString(), mGesture);
store.save();
setResult(RESULT_OK);
final String path = new File(Environment.getExternalStorageDirectory(),
"gestures").getAbsolutePath();
Toast.makeText(this, getString(R.string.save_success, path), Toast.LENGTH_LONG).show();
} else {
setResult(RESULT_CANCELED);
}
finish();
}
@SuppressWarnings({"UnusedDeclaration"})
public void cancelGesture(View v) {
setResult(RESULT_CANCELED);
finish();
}
private class GesturesProcessor implements GestureOverlayView.OnGestureListener {
public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {
mDoneButton.setEnabled(false);
mGesture = null;
}
public void onGesture(GestureOverlayView overlay, MotionEvent event) {
}
public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
mGesture = overlay.getGesture();
if (mGesture.getLength() < LENGTH_THRESHOLD) {
overlay.clear(false);
}
mDoneButton.setEnabled(true);
}
public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {
}
}
}

View File

@@ -0,0 +1,437 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.gesture.builder;
import android.app.Dialog;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.os.Bundle;
import android.os.AsyncTask;
import android.os.Environment;
import android.view.View;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.gesture.GestureLibrary;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.ArrayAdapter;
import android.content.DialogInterface;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.text.TextUtils;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;
import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Comparator;
import java.util.Set;
import java.io.File;
public class GestureBuilderActivity extends ListActivity {
private static final int STATUS_SUCCESS = 0;
private static final int STATUS_CANCELLED = 1;
private static final int STATUS_NO_STORAGE = 2;
private static final int STATUS_NOT_LOADED = 3;
private static final int MENU_ID_RENAME = 1;
private static final int MENU_ID_REMOVE = 2;
private static final int DIALOG_RENAME_GESTURE = 1;
private static final int REQUEST_NEW_GESTURE = 1;
// Type: long (id)
private static final String GESTURES_INFO_ID = "gestures.info_id";
private final File mStoreFile = new File(Environment.getExternalStorageDirectory(), "gestures");
private final Comparator<NamedGesture> mSorter = new Comparator<NamedGesture>() {
public int compare(NamedGesture object1, NamedGesture object2) {
return object1.name.compareTo(object2.name);
}
};
private static GestureLibrary sStore;
private GesturesAdapter mAdapter;
private GesturesLoadTask mTask;
private TextView mEmpty;
private Dialog mRenameDialog;
private EditText mInput;
private NamedGesture mCurrentRenameGesture;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gestures_list);
mAdapter = new GesturesAdapter(this);
setListAdapter(mAdapter);
if (sStore == null) {
sStore = GestureLibraries.fromFile(mStoreFile);
}
mEmpty = (TextView) findViewById(android.R.id.empty);
loadGestures();
registerForContextMenu(getListView());
}
static GestureLibrary getStore() {
return sStore;
}
@SuppressWarnings({"UnusedDeclaration"})
public void reloadGestures(View v) {
loadGestures();
}
@SuppressWarnings({"UnusedDeclaration"})
public void addGesture(View v) {
Intent intent = new Intent(this, CreateGestureActivity.class);
startActivityForResult(intent, REQUEST_NEW_GESTURE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_NEW_GESTURE:
loadGestures();
break;
}
}
}
private void loadGestures() {
if (mTask != null && mTask.getStatus() != GesturesLoadTask.Status.FINISHED) {
mTask.cancel(true);
}
mTask = (GesturesLoadTask) new GesturesLoadTask().execute();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mTask != null && mTask.getStatus() != GesturesLoadTask.Status.FINISHED) {
mTask.cancel(true);
mTask = null;
}
cleanupRenameDialog();
}
private void checkForEmpty() {
if (mAdapter.getCount() == 0) {
mEmpty.setText(R.string.gestures_empty);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mCurrentRenameGesture != null) {
outState.putLong(GESTURES_INFO_ID, mCurrentRenameGesture.gesture.getID());
}
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
long id = state.getLong(GESTURES_INFO_ID, -1);
if (id != -1) {
final Set<String> entries = sStore.getGestureEntries();
out: for (String name : entries) {
for (Gesture gesture : sStore.getGestures(name)) {
if (gesture.getID() == id) {
mCurrentRenameGesture = new NamedGesture();
mCurrentRenameGesture.name = name;
mCurrentRenameGesture.gesture = gesture;
break out;
}
}
}
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
menu.setHeaderTitle(((TextView) info.targetView).getText());
menu.add(0, MENU_ID_RENAME, 0, R.string.gestures_rename);
menu.add(0, MENU_ID_REMOVE, 0, R.string.gestures_delete);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
final AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)
item.getMenuInfo();
final NamedGesture gesture = (NamedGesture) menuInfo.targetView.getTag();
switch (item.getItemId()) {
case MENU_ID_RENAME:
renameGesture(gesture);
return true;
case MENU_ID_REMOVE:
deleteGesture(gesture);
return true;
}
return super.onContextItemSelected(item);
}
private void renameGesture(NamedGesture gesture) {
mCurrentRenameGesture = gesture;
showDialog(DIALOG_RENAME_GESTURE);
}
@Override
protected Dialog onCreateDialog(int id) {
if (id == DIALOG_RENAME_GESTURE) {
return createRenameDialog();
}
return super.onCreateDialog(id);
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
if (id == DIALOG_RENAME_GESTURE) {
mInput.setText(mCurrentRenameGesture.name);
}
}
private Dialog createRenameDialog() {
final View layout = View.inflate(this, R.layout.dialog_rename, null);
mInput = (EditText) layout.findViewById(R.id.name);
((TextView) layout.findViewById(R.id.label)).setText(R.string.gestures_rename_label);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(0);
builder.setTitle(getString(R.string.gestures_rename_title));
builder.setCancelable(true);
builder.setOnCancelListener(new Dialog.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
cleanupRenameDialog();
}
});
builder.setNegativeButton(getString(R.string.cancel_action),
new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
cleanupRenameDialog();
}
}
);
builder.setPositiveButton(getString(R.string.rename_action),
new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
changeGestureName();
}
}
);
builder.setView(layout);
return builder.create();
}
private void changeGestureName() {
final String name = mInput.getText().toString();
if (!TextUtils.isEmpty(name)) {
final NamedGesture renameGesture = mCurrentRenameGesture;
final GesturesAdapter adapter = mAdapter;
final int count = adapter.getCount();
// Simple linear search, there should not be enough items to warrant
// a more sophisticated search
for (int i = 0; i < count; i++) {
final NamedGesture gesture = adapter.getItem(i);
if (gesture.gesture.getID() == renameGesture.gesture.getID()) {
sStore.removeGesture(gesture.name, gesture.gesture);
gesture.name = mInput.getText().toString();
sStore.addGesture(gesture.name, gesture.gesture);
break;
}
}
adapter.notifyDataSetChanged();
}
mCurrentRenameGesture = null;
}
private void cleanupRenameDialog() {
if (mRenameDialog != null) {
mRenameDialog.dismiss();
mRenameDialog = null;
}
mCurrentRenameGesture = null;
}
private void deleteGesture(NamedGesture gesture) {
sStore.removeGesture(gesture.name, gesture.gesture);
sStore.save();
final GesturesAdapter adapter = mAdapter;
adapter.setNotifyOnChange(false);
adapter.remove(gesture);
adapter.sort(mSorter);
checkForEmpty();
adapter.notifyDataSetChanged();
Toast.makeText(this, R.string.gestures_delete_success, Toast.LENGTH_SHORT).show();
}
private class GesturesLoadTask extends AsyncTask<Void, NamedGesture, Integer> {
private int mThumbnailSize;
private int mThumbnailInset;
private int mPathColor;
@Override
protected void onPreExecute() {
super.onPreExecute();
final Resources resources = getResources();
mPathColor = resources.getColor(R.color.gesture_color);
mThumbnailInset = (int) resources.getDimension(R.dimen.gesture_thumbnail_inset);
mThumbnailSize = (int) resources.getDimension(R.dimen.gesture_thumbnail_size);
findViewById(R.id.addButton).setEnabled(false);
findViewById(R.id.reloadButton).setEnabled(false);
mAdapter.setNotifyOnChange(false);
mAdapter.clear();
}
protected Integer doInBackground(Void... params) {
if (isCancelled()) return STATUS_CANCELLED;
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return STATUS_NO_STORAGE;
}
final GestureLibrary store = sStore;
if (store.load()) {
for (String name : store.getGestureEntries()) {
if (isCancelled()) break;
for (Gesture gesture : store.getGestures(name)) {
final Bitmap bitmap = gesture.toBitmap(mThumbnailSize, mThumbnailSize,
mThumbnailInset, mPathColor);
final NamedGesture namedGesture = new NamedGesture();
namedGesture.gesture = gesture;
namedGesture.name = name;
mAdapter.addBitmap(namedGesture.gesture.getID(), bitmap);
publishProgress(namedGesture);
}
}
return STATUS_SUCCESS;
}
return STATUS_NOT_LOADED;
}
@Override
protected void onProgressUpdate(NamedGesture... values) {
super.onProgressUpdate(values);
final GesturesAdapter adapter = mAdapter;
adapter.setNotifyOnChange(false);
for (NamedGesture gesture : values) {
adapter.add(gesture);
}
adapter.sort(mSorter);
adapter.notifyDataSetChanged();
}
@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
if (result == STATUS_NO_STORAGE) {
mList.setVisibility(View.GONE);
mEmpty.setVisibility(View.VISIBLE);
mEmpty.setText(getString(R.string.gestures_error_loading,
mStoreFile.getAbsolutePath()));
} else {
findViewById(R.id.addButton).setEnabled(true);
findViewById(R.id.reloadButton).setEnabled(true);
checkForEmpty();
}
}
}
static class NamedGesture {
String name;
Gesture gesture;
}
private class GesturesAdapter extends ArrayAdapter<NamedGesture> {
private final LayoutInflater mInflater;
private final Map<Long, Drawable> mThumbnails = Collections.synchronizedMap(
new HashMap<Long, Drawable>());
public GesturesAdapter(Context context) {
super(context, 0);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
void addBitmap(Long id, Bitmap bitmap) {
mThumbnails.put(id, new BitmapDrawable(bitmap));
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.gestures_item, parent, false);
}
final NamedGesture gesture = getItem(position);
final TextView label = (TextView) convertView;
label.setTag(gesture);
label.setText(gesture.name);
label.setCompoundDrawablesWithIntrinsicBounds(mThumbnails.get(gesture.gesture.getID()),
null, null, null);
return convertView;
}
}
}

View File

@@ -45,10 +45,6 @@
<string name="summary_on_fancy_ime_animations">Use fancier animations for input method windows</string> <string name="summary_on_fancy_ime_animations">Use fancier animations for input method windows</string>
<string name="summary_off_fancy_ime_animations">Use normal animations for input method windows</string> <string name="summary_off_fancy_ime_animations">Use normal animations for input method windows</string>
<string name="title_fancy_rotation_animations">Fancy rotation animations</string>
<string name="summary_on_fancy_rotation_animations">Use fancier animations for screen rotation</string>
<string name="summary_off_fancy_rotation_animations">Use normal animations for screen rotation</string>
<string name="title_haptic_feedback">Haptic feedback</string> <string name="title_haptic_feedback">Haptic feedback</string>
<string name="summary_on_haptic_feedback">Use haptic feedback with user interaction</string> <string name="summary_on_haptic_feedback">Use haptic feedback with user interaction</string>
<string name="summary_off_haptic_feedback">Use haptic feedback with user interaction</string> <string name="summary_off_haptic_feedback">Use haptic feedback with user interaction</string>
@@ -68,4 +64,11 @@
<string name="summary_off_maps_compass">Compass is not displayed in Maps</string> <string name="summary_off_maps_compass">Compass is not displayed in Maps</string>
<string name="development_settings_show_maps_compass_text">Show compass in Maps</string> <string name="development_settings_show_maps_compass_text">Show compass in Maps</string>
<!-- Sound & display settings screen, compatibility mode check box label -->
<string name="compatibility_mode_title">Compatibility Mode</string>
<!-- Sound & display settings screen, compatibility mode option summary text when check box is selected -->
<string name="compatibility_mode_summary_on">Run older apps in Compatibility mode. This require rebooting. </string>
<!-- Sound & display settings screen, compatibility mode option summary text when check box is clear -->
<string name="compatibility_mode_summary_off">Run older apps in Compatibility mode. This require rebooting. </string>
</resources> </resources>

View File

@@ -72,12 +72,6 @@
android:summaryOn="@string/summary_on_fancy_ime_animations" android:summaryOn="@string/summary_on_fancy_ime_animations"
android:summaryOff="@string/summary_off_fancy_ime_animations"/> android:summaryOff="@string/summary_off_fancy_ime_animations"/>
<CheckBoxPreference
android:key="fancy_rotation_animations"
android:title="@string/title_fancy_rotation_animations"
android:summaryOn="@string/summary_on_fancy_rotation_animations"
android:summaryOff="@string/summary_off_fancy_rotation_animations"/>
<ListPreference <ListPreference
android:key="font_size" android:key="font_size"
android:title="@string/title_font_size" android:title="@string/title_font_size"
@@ -100,6 +94,12 @@
android:summaryOn="@string/summary_on_haptic_feedback" android:summaryOn="@string/summary_on_haptic_feedback"
android:summaryOff="@string/summary_off_haptic_feedback"/> android:summaryOff="@string/summary_off_haptic_feedback"/>
<CheckBoxPreference
android:key="compatibility_mode"
android:title="@string/compatibility_mode_title"
android:summaryOn="@string/compatibility_mode_summary_on"
android:summaryOff="@string/compatibility_mode_summary_off" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory

View File

@@ -54,23 +54,23 @@ public class SpareParts extends PreferenceActivity
private static final String WINDOW_ANIMATIONS_PREF = "window_animations"; private static final String WINDOW_ANIMATIONS_PREF = "window_animations";
private static final String TRANSITION_ANIMATIONS_PREF = "transition_animations"; private static final String TRANSITION_ANIMATIONS_PREF = "transition_animations";
private static final String FANCY_IME_ANIMATIONS_PREF = "fancy_ime_animations"; private static final String FANCY_IME_ANIMATIONS_PREF = "fancy_ime_animations";
private static final String FANCY_ROTATION_ANIMATIONS_PREF = "fancy_rotation_animations";
private static final String HAPTIC_FEEDBACK_PREF = "haptic_feedback"; private static final String HAPTIC_FEEDBACK_PREF = "haptic_feedback";
private static final String FONT_SIZE_PREF = "font_size"; private static final String FONT_SIZE_PREF = "font_size";
private static final String END_BUTTON_PREF = "end_button"; private static final String END_BUTTON_PREF = "end_button";
private static final String MAPS_COMPASS_PREF = "maps_compass"; private static final String MAPS_COMPASS_PREF = "maps_compass";
private static final String KEY_COMPATIBILITY_MODE = "compatibility_mode";
private final Configuration mCurConfig = new Configuration(); private final Configuration mCurConfig = new Configuration();
private ListPreference mWindowAnimationsPref; private ListPreference mWindowAnimationsPref;
private ListPreference mTransitionAnimationsPref; private ListPreference mTransitionAnimationsPref;
private CheckBoxPreference mFancyImeAnimationsPref; private CheckBoxPreference mFancyImeAnimationsPref;
private CheckBoxPreference mFancyRotationAnimationsPref;
private CheckBoxPreference mHapticFeedbackPref; private CheckBoxPreference mHapticFeedbackPref;
private ListPreference mFontSizePref; private ListPreference mFontSizePref;
private ListPreference mEndButtonPref; private ListPreference mEndButtonPref;
private CheckBoxPreference mShowMapsCompassPref; private CheckBoxPreference mShowMapsCompassPref;
private CheckBoxPreference mCompatibilityMode;
private IWindowManager mWindowManager; private IWindowManager mWindowManager;
public static boolean updatePreferenceToSpecificActivityOrRemove(Context context, public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
@@ -120,14 +120,17 @@ public class SpareParts extends PreferenceActivity
mTransitionAnimationsPref = (ListPreference) prefSet.findPreference(TRANSITION_ANIMATIONS_PREF); mTransitionAnimationsPref = (ListPreference) prefSet.findPreference(TRANSITION_ANIMATIONS_PREF);
mTransitionAnimationsPref.setOnPreferenceChangeListener(this); mTransitionAnimationsPref.setOnPreferenceChangeListener(this);
mFancyImeAnimationsPref = (CheckBoxPreference) prefSet.findPreference(FANCY_IME_ANIMATIONS_PREF); mFancyImeAnimationsPref = (CheckBoxPreference) prefSet.findPreference(FANCY_IME_ANIMATIONS_PREF);
mFancyRotationAnimationsPref = (CheckBoxPreference) prefSet.findPreference(FANCY_ROTATION_ANIMATIONS_PREF);
mHapticFeedbackPref = (CheckBoxPreference) prefSet.findPreference(HAPTIC_FEEDBACK_PREF); mHapticFeedbackPref = (CheckBoxPreference) prefSet.findPreference(HAPTIC_FEEDBACK_PREF);
mFontSizePref = (ListPreference) prefSet.findPreference(FONT_SIZE_PREF); mFontSizePref = (ListPreference) prefSet.findPreference(FONT_SIZE_PREF);
mFontSizePref.setOnPreferenceChangeListener(this); mFontSizePref.setOnPreferenceChangeListener(this);
mEndButtonPref = (ListPreference) prefSet.findPreference(END_BUTTON_PREF); mEndButtonPref = (ListPreference) prefSet.findPreference(END_BUTTON_PREF);
mEndButtonPref.setOnPreferenceChangeListener(this); mEndButtonPref.setOnPreferenceChangeListener(this);
mShowMapsCompassPref = (CheckBoxPreference) prefSet.findPreference(MAPS_COMPASS_PREF); mShowMapsCompassPref = (CheckBoxPreference) prefSet.findPreference(MAPS_COMPASS_PREF);
mCompatibilityMode = (CheckBoxPreference) findPreference(KEY_COMPATIBILITY_MODE);
mCompatibilityMode.setPersistent(false);
mCompatibilityMode.setChecked(Settings.System.getInt(getContentResolver(),
Settings.System.COMPATIBILITY_MODE, 1) != 0);
mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
final PreferenceGroup parentPreference = getPreferenceScreen(); final PreferenceGroup parentPreference = getPreferenceScreen();
@@ -146,9 +149,6 @@ public class SpareParts extends PreferenceActivity
mFancyImeAnimationsPref.setChecked(Settings.System.getInt( mFancyImeAnimationsPref.setChecked(Settings.System.getInt(
getContentResolver(), getContentResolver(),
Settings.System.FANCY_IME_ANIMATIONS, 0) != 0); Settings.System.FANCY_IME_ANIMATIONS, 0) != 0);
mFancyRotationAnimationsPref.setChecked(Settings.System.getInt(
getContentResolver(),
"fancy_rotation_anim", 0) != 0);
mHapticFeedbackPref.setChecked(Settings.System.getInt( mHapticFeedbackPref.setChecked(Settings.System.getInt(
getContentResolver(), getContentResolver(),
Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) != 0); Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) != 0);
@@ -171,11 +171,21 @@ public class SpareParts extends PreferenceActivity
} else if (preference == mEndButtonPref) { } else if (preference == mEndButtonPref) {
writeEndButtonPreference(objValue); writeEndButtonPreference(objValue);
} }
// always let the preference setting proceed. // always let the preference setting proceed.
return true; return true;
} }
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mCompatibilityMode) {
Settings.System.putInt(getContentResolver(),
Settings.System.COMPATIBILITY_MODE,
mCompatibilityMode.isChecked() ? 1 : 0);
return true;
}
return false;
}
public void writeAnimationPreference(int which, Object objValue) { public void writeAnimationPreference(int which, Object objValue) {
try { try {
float val = Float.parseFloat(objValue.toString()); float val = Float.parseFloat(objValue.toString());
@@ -247,10 +257,6 @@ public class SpareParts extends PreferenceActivity
Settings.System.putInt(getContentResolver(), Settings.System.putInt(getContentResolver(),
Settings.System.FANCY_IME_ANIMATIONS, Settings.System.FANCY_IME_ANIMATIONS,
mFancyImeAnimationsPref.isChecked() ? 1 : 0); mFancyImeAnimationsPref.isChecked() ? 1 : 0);
} else if (FANCY_ROTATION_ANIMATIONS_PREF.equals(key)) {
Settings.System.putInt(getContentResolver(),
"fancy_rotation_anim",
mFancyRotationAnimationsPref.isChecked() ? 1 : 0);
} else if (HAPTIC_FEEDBACK_PREF.equals(key)) { } else if (HAPTIC_FEEDBACK_PREF.equals(key)) {
Settings.System.putInt(getContentResolver(), Settings.System.putInt(getContentResolver(),
Settings.System.HAPTIC_FEEDBACK_ENABLED, Settings.System.HAPTIC_FEEDBACK_ENABLED,

View File

@@ -19,7 +19,7 @@ package com.android.commands.monkey;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
import android.app.IActivityManager; import android.app.IActivityManager;
import android.app.IActivityWatcher; import android.app.IActivityController;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
import android.content.pm.IPackageManager; import android.content.pm.IPackageManager;
@@ -88,10 +88,10 @@ public class Monkey {
/** This is set when we would like to abort the running of the monkey. */ /** This is set when we would like to abort the running of the monkey. */
private boolean mAbort; private boolean mAbort;
/** This is set by the ActivityWatcher thread to request collection of ANR trace files */ /** This is set by the ActivityController thread to request collection of ANR trace files */
private boolean mRequestAnrTraces = false; private boolean mRequestAnrTraces = false;
/** This is set by the ActivityWatcher thread to request a "dumpsys meminfo" */ /** This is set by the ActivityController thread to request a "dumpsys meminfo" */
private boolean mRequestDumpsysMemInfo = false; private boolean mRequestDumpsysMemInfo = false;
/** Kill the process after a timeout or crash. */ /** Kill the process after a timeout or crash. */
@@ -135,7 +135,7 @@ public class Monkey {
/** /**
* Monitor operations happening in the system. * Monitor operations happening in the system.
*/ */
private class ActivityWatcher extends IActivityWatcher.Stub { private class ActivityController extends IActivityController.Stub {
public boolean activityStarting(Intent intent, String pkg) { public boolean activityStarting(Intent intent, String pkg) {
boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_STARTS != 0); boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_STARTS != 0);
if (mVerbose > 0) { if (mVerbose > 0) {
@@ -246,7 +246,7 @@ public class Monkey {
/** /**
* Run "dumpsys meminfo" * Run "dumpsys meminfo"
* *
* NOTE: You cannot perform a dumpsys call from the ActivityWatcher callback, as it will * NOTE: You cannot perform a dumpsys call from the ActivityController callback, as it will
* deadlock. This should only be called from the main loop of the monkey. * deadlock. This should only be called from the main loop of the monkey.
*/ */
private void reportDumpsysMemInfo() { private void reportDumpsysMemInfo() {
@@ -425,7 +425,7 @@ public class Monkey {
} }
try { try {
mAm.setActivityWatcher(null); mAm.setActivityController(null);
mNetworkMonitor.unregister(mAm); mNetworkMonitor.unregister(mAm);
} catch (RemoteException e) { } catch (RemoteException e) {
// just in case this was latent (after mCount cycles), make sure // just in case this was latent (after mCount cycles), make sure
@@ -608,7 +608,7 @@ public class Monkey {
} }
try { try {
mAm.setActivityWatcher(new ActivityWatcher()); mAm.setActivityController(new ActivityController());
mNetworkMonitor.register(mAm); mNetworkMonitor.register(mAm);
} catch (RemoteException e) { } catch (RemoteException e) {
System.err.println("** Failed talking with activity manager!"); System.err.println("** Failed talking with activity manager!");

View File

@@ -404,7 +404,7 @@ compile-c-source = $(eval $(call ev-compile-c-source,$1,$(1:%.c=%.o)))
# Usage : $(call compile-s-source,<srcfile>) # Usage : $(call compile-s-source,<srcfile>)
# Rationale : Setup everything required to build a single Assembly source file # Rationale : Setup everything required to build a single Assembly source file
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
compile-s-source = $(eval $(call ev-compile-s-source,$1,$(1:%.S=%.o))) compile-s-source = $(eval $(call ev-compile-c-source,$1,$(1:%.S=%.o)))
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------

View File

@@ -30,5 +30,7 @@ current version
- Add support for LOCAL_C_INCLUDES in Android.mk - Add support for LOCAL_C_INCLUDES in Android.mk
- Fix compilation of assembler files (e.g. foo.S)
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
android-1.5_r1 released. android-1.5_r1 released.

View File

@@ -77,6 +77,7 @@ function nothing() {}
<li><a href="<?cs var:toroot ?>guide/instrumentation_testing.html">Instrumentation Testing</a></li> <li><a href="<?cs var:toroot ?>guide/instrumentation_testing.html">Instrumentation Testing</a></li>
<li><a href="<?cs var:toroot ?>guide/debugging_gdb.html">Debugging with GDB</a></li> <li><a href="<?cs var:toroot ?>guide/debugging_gdb.html">Debugging with GDB</a></li>
<li><a href="<?cs var:toroot ?>guide/debugging_native.html">Debugging Native Code</a></li> <li><a href="<?cs var:toroot ?>guide/debugging_native.html">Debugging Native Code</a></li>
<li><a href="<?cs var:toroot ?>guide/tcpdump.html">Debugging with tcpdump</a></li>
</ul> </ul>
</li> </li>

89
pdk/docs/guide/tcpdump.jd Executable file
View File

@@ -0,0 +1,89 @@
page.title=Debugging with tcpdump and other tools
pdk.version=1.0
@jd:body
<a name="toc"/>
<div style="padding:10px">
<a href="#installing">Installing tcpdump</a><BR>
<a href="#running">Running tcpdump</a><br/>
<a href="#other">Other network debugging commands</a><br/>
</div>
<a name="installing"></a>
<h3>Installing tcpdump</h3>
<h4>Pushing the binary to an existing device</h4>
<p>Download tcpdump from <a href="http://www.tcpdump.org/">http://www.tcpdump.org/</a>, then execute:</p>
<pre>
adb root
adb remount
adb push /wherever/you/put/tcpdump /system/xbin/tcpdump
adb shell chmod 6755 /data/local/tmp/tcpdump
</pre>
<h4>Including tcpdump in the build image</h4>
<p>If you are running your own build, execute:</p>
<pre>
mmm external/tcpdump # install the binary in out/.../system/xbin
make snod # build a new system.img that includes it
</pre>
<p>Flash the device as usual, for example, <code>fastboot flashball</code>.</p>
<p>If you want to build tcpdump by default, add <code>CUSTOM_TARGETS += tcpdump</code> to your <code>buildspec.mk</code>.</p>
<h3><a name="running"></a>Running tcpdump</h3>
<p>You need to have root access on your device. </p>
<h4>Batch mode capture</h4>
<p>The typical procedure is to capture packets to a file and then examine the file on the desktop, as illustrated below:</p>
<pre>
adb shell tcpdump -i any -p -s 0 -w /sdcard/capture.pcap
# "-i any": listen on any network interface
# "-p": disable promiscuous mode (doesn't work anyway)
# "-s 0": capture the entire packet
# "-w": write packets to a file (rather than printing to stdout)
... do whatever you want to capture, then ^C to stop it ...
adb pull /sdcard/capture.pcap .
sudo apt-get install wireshark # or ethereal, if you're still on dapper
wireshark capture.pcap # or ethereal
... look at your packets and be wise ...
</pre>
<p>You can run <code>tcpdump</code> in the background from an interactive shell or from Terminal. By default, <code>tcpdump</code> captures all traffic without filtering. If you prefer, add an expression like port 80 to the <code>tcpdump</code> command line.</p>
<h4>Real time packet monitoring</h4>
<p>Execute the following if you would like to watch packets go by rather than capturing them to a file (<code>-n</code> skips DNS lookups. <code>-s 0</code> captures the entire packet rather than just the header):</p>
<pre>
adb shell tcpdump -n -s 0
</pre>
<p>Typical <code>tcpdump</code> options apply. For example, if you want to see HTTP traffic:</p>
<pre>
adb shell tcpdump -X -n -s 0 port 80
</pre>
<p>You can also monitor packets with <code>wireshark</code> or <code>ethereal</code>, as shown below:</p>
<pre>
# In one shell, start tcpdump.
adb shell "tcpdump -n -s 0 -w - | nc -l -p 11233"
# In a separate shell, forward data and run ethereal.
adb forward tcp:11233 tcp:11233 && nc 127.0.0.1 11233 | ethereal -k -S -i -
</pre>
<p>Note that you can't restart capture via <code>ethereal</code>. If anything goes wrong, you will need to rerun both commands.</p>
<p>For more immediate output, add <code>-l</code> to the <code>tcpdump</code> command line, but this can cause <code>adb</code> to choke (it helps to use a nonzero argument for <code>-s</code> to limit the amount of data captured per packet; <code>-s 100</code> is sufficient if you just want to see headers).</p>
<h4>Disabling encryption</h4>
<p>If your service runs over <code>https</code>, <code>tcpdump</code> is of limited use. In this case, you can rewrite some service URLs to use <code>http</code>, for example:</p>
<pre>
vendor/google/tools/override-gservices url:calendar_sync_https_proxy \
https://www.google.com/calendar rewrite http://android.clients.google.com/proxy/calendar
</pre>
<h3><a name="other"></a>Other network debugging commands</h3>
<h4>On the device:</h4>
<ul>
<li><code>ifconfig interface</code>: note that unlike Linux, you need to give <code>ifconfig</code> an argument</li>
<li><code>netcfg</code>: lists interfaces and IP addresses</li>
<li><code>iftop</code>: like top for network</li>
<li><code>route</code>: examine the routing table</li>
<li><code>netstat</code>: see active network connections</li>
<li><code>nc</code>: <code>netcat</code> connection utility</li>
</ul>
<h4>On the desktop:</h4>
<ul>
<li> <code>curl</code>: fetch URLs directly to emulate device requests</li>
</ul>

View File

@@ -0,0 +1,60 @@
#!/usr/bin/python2.4
#
#
# Copyright 2009, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
#
# 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.
"""In memory representation of AndroidManifest.xml file.
Specification of AndroidManifest.xml can be found at
http://developer.android.com/guide/topics/manifest/manifest-intro.html
"""
# python imports
import xml.dom.minidom
import xml.parsers
class AndroidManifest(object):
"""In memory representation of AndroidManifest.xml file."""
FILENAME = "AndroidManifest.xml"
def __init__(self, app_path=None):
if app_path:
self.ParseManifest(app_path)
def GetPackageName(self):
"""Retrieve package name defined at <manifest package="...">.
Returns:
Package name if defined, otherwise None
"""
manifests = self._dom.getElementsByTagName("manifest")
if not manifests or not manifests[0].getAttribute("package"):
return None
return manifests[0].getAttribute("package")
def ParseManifest(self, app_path):
"""Parse AndroidManifest.xml at the specified path.
Args:
app_path: path to folder containing AndroidManifest.xml
Raises:
IOError: AndroidManifest.xml cannot be found at given path, or cannot be
opened for reading
"""
self.app_path = app_path.rstrip("/")
self.manifest_path = "%s/%s" % (self.app_path, self.FILENAME)
self._dom = xml.dom.minidom.parse(self.manifest_path)

96
testrunner/android_mk.py Normal file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/python2.4
#
#
# Copyright 2009, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
#
# 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.
"""In memory representation of Android.mk file.
Specifications for Android.mk can be found at
development/ndk/docs/ANDROID-MK.txt
"""
import re
from sets import Set
class AndroidMK(object):
"""In memory representation of Android.mk file."""
_RE_INCLUDE = re.compile(r'include\s+\$\((.+)\)')
_VAR_DELIMITER = ":="
FILENAME = "Android.mk"
CERTIFICATE = "LOCAL_CERTIFICATE"
PACKAGE_NAME = "LOCAL_PACKAGE_NAME"
def __init__(self, app_path=None):
self._includes = Set() # variables included in makefile
self._variables = {} # variables defined in makefile
if app_path:
self.ParseMK(app_path)
def _ProcessMKLine(self, line):
"""Add a variable definition or include.
Ignores unrecognized lines.
Args:
line: line of text from makefile
"""
m = self._RE_INCLUDE.match(line)
if m:
self._includes.add(m.group(1))
else:
parts = line.split(self._VAR_DELIMITER)
if len(parts) > 1:
self._variables[parts[0].strip()] = parts[1].strip()
def GetVariable(self, identifier):
"""Retrieve makefile variable.
Args:
identifier: name of variable to retrieve
Returns:
value of specified identifier, None if identifier not found in makefile
"""
# use dict.get(x) rather than dict[x] to avoid KeyError exception,
# so None is returned if identifier not found
return self._variables.get(identifier, None)
def HasInclude(self, identifier):
"""Check variable is included in makefile.
Args:
identifer: name of variable to check
Returns:
True if identifer is included in makefile, otherwise False
"""
return identifier in self._includes
def ParseMK(self, app_path):
"""Parse Android.mk at the specified path.
Args:
app_path: path to folder containing Android.mk
Raises:
IOError: Android.mk cannot be found at given path, or cannot be opened
for reading
"""
self.app_path = app_path.rstrip("/")
self.mk_path = "%s/%s" % (self.app_path, self.FILENAME)
mk = open(self.mk_path)
for line in mk:
self._ProcessMKLine(line)
mk.close()

245
testrunner/create_test.py Executable file
View File

@@ -0,0 +1,245 @@
#!/usr/bin/python2.4
#
#
# Copyright 2009, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
#
# 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.
"""Utility to create Android project files for tests."""
# python imports
import datetime
import optparse
import os
import string
import sys
# local imports
import android_mk
import android_manifest
class TestsConsts(object):
"""Constants for test Android.mk and AndroidManifest.xml creation."""
MK_BUILD_INCLUDE = "call all-makefiles-under,$(LOCAL_PATH)"
MK_BUILD_STRING = "\ninclude $(%s)\n" % MK_BUILD_INCLUDE
TEST_MANIFEST_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) $YEAR The Android Open Source Project
Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="$PACKAGE_NAME.tests">
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="$PACKAGE_NAME"
android:label="Tests for $MODULE_NAME">
</instrumentation>
</manifest>
"""
TEST_MK_TEMPLATE = """LOCAL_PATH := $$(call my-dir)
include $$(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_SRC_FILES := $$(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ${MODULE_NAME}Tests${CERTIFICATE}
LOCAL_INSTRUMENTATION_FOR := ${MODULE_NAME}
LOCAL_SDK_VERSION := current
include $$(BUILD_PACKAGE)
"""
TESTS_FOLDER = "tests"
def _GenerateTestManifest(manifest, module_name, mapping=None):
"""Create and populate tests/AndroidManifest.xml with variable values from
Android.mk and AndroidManifest.xml.
Does nothing if tests/AndroidManifest.xml already exists.
Args:
manifest: AndroidManifest object for application manifest
module_name: module name used for labelling
mapping: optional user defined mapping of variable values, replaces values
extracted from AndroidManifest.xml
Raises:
IOError: tests/AndroidManifest.xml cannot be opened for writing
"""
# skip if file already exists
tests_path = "%s/%s" % (manifest.app_path, TestsConsts.TESTS_FOLDER)
tests_manifest_path = "%s/%s" % (tests_path, manifest.FILENAME)
if os.path.exists(tests_manifest_path):
_PrintMessage("%s already exists, not overwritten" % tests_manifest_path)
return
if not mapping:
package_name = manifest.GetPackageName()
mapping = {"PACKAGE_NAME":package_name, "MODULE_NAME":module_name,
"YEAR":datetime.date.today().year}
output = string.Template(TestsConsts.TEST_MANIFEST_TEMPLATE).substitute(mapping)
# create tests folder if not existent
if not os.path.exists(tests_path):
os.mkdir(tests_path)
# write tests/AndroidManifest.xml
tests_manifest = open(tests_manifest_path, mode="w")
tests_manifest.write(output)
tests_manifest.close()
_PrintMessage("Created %s" % tests_manifest_path)
def _GenerateTestMK(mk, mapping=None):
"""Create and populate tests/Android.mk with variable values from Android.mk.
Does nothing if tests/Android.mk already exists.
Args:
mk: AndroidMK object for application makefile
mapping: optional user defined mapping of variable values, replaces
values stored in mk
Raises:
IOError: tests/Android.mk cannot be opened for writing
"""
# skip if file already exists
tests_path = "%s/%s" % (mk.app_path, TestsConsts.TESTS_FOLDER)
tests_mk_path = "%s/%s" % (tests_path, mk.FILENAME)
if os.path.exists(tests_mk_path):
_PrintMessage("%s already exists, not overwritten" % tests_mk_path)
return
# append test build if not existent in makefile
if not mk.HasInclude(TestsConsts.MK_BUILD_INCLUDE):
mk_path = "%s/%s" % (mk.app_path, mk.FILENAME)
mk_file = open(mk_path, mode="a")
mk_file.write(TestsConsts.MK_BUILD_STRING)
mk_file.close()
# construct tests/Android.mk
# include certificate definition if existent in makefile
certificate = mk.GetVariable(mk.CERTIFICATE)
if certificate:
cert_definition = ("\n%s := %s" % (mk.CERTIFICATE, certificate))
else:
cert_definition = ""
if not mapping:
module_name = mk.GetVariable(mk.PACKAGE_NAME)
mapping = {"MODULE_NAME":module_name, "CERTIFICATE":cert_definition}
output = string.Template(TestsConsts.TEST_MK_TEMPLATE).substitute(mapping)
# create tests folder if not existent
if not os.path.exists(tests_path):
os.mkdir(tests_path)
# write tests/Android.mk to disk
tests_mk = open(tests_mk_path, mode="w")
tests_mk.write(output)
tests_mk.close()
_PrintMessage("Created %s" % tests_mk_path)
def _ParseArgs(argv):
"""Parse the command line arguments.
Args:
argv: the list of command line arguments
Returns:
a tuple of options and individual command line arguments.
"""
parser = optparse.OptionParser(usage="%s <app_path>" % sys.argv[0])
options, args = parser.parse_args(argv)
if len(args) < 1:
_PrintError("Error: Incorrect syntax")
parser.print_usage()
sys.exit()
return (options, args)
def _PrintMessage(msg):
print >> sys.stdout, msg
def _PrintError(msg):
print >> sys.stderr, msg
def _ValidateInputFiles(mk, manifest):
"""Verify that required variables are defined in input files.
Args:
mk: AndroidMK object for application makefile
manifest: AndroidManifest object for application manifest
Raises:
RuntimeError: mk does not define LOCAL_PACKAGE_NAME or
manifest does not define package variable
"""
module_name = mk.GetVariable(mk.PACKAGE_NAME)
if not module_name:
raise RuntimeError("Variable %s missing from %s" %
(mk.PACKAGE_NAME, mk.FILENAME))
package_name = manifest.GetPackageName()
if not package_name:
raise RuntimeError("Variable package missing from %s" % manifest.FILENAME)
def main(argv):
options, args = _ParseArgs(argv)
app_path = args[0];
if not os.path.exists(app_path):
_PrintError("Error: Application path %s not found" % app_path)
sys.exit()
try:
mk = android_mk.AndroidMK(app_path=app_path)
manifest = android_manifest.AndroidManifest(app_path=app_path)
_ValidateInputFiles(mk, manifest)
module_name = mk.GetVariable(mk.PACKAGE_NAME)
_GenerateTestMK(mk)
_GenerateTestManifest(manifest, module_name)
except Exception, e:
_PrintError("Error: %s" % e)
_PrintError("Error encountered, script aborted")
sys.exit()
src_path = app_path + "/tests/src"
if not os.path.exists(src_path):
os.mkdir(src_path)
if __name__ == "__main__":
main(sys.argv[1:])

View File

@@ -88,6 +88,13 @@ See test_defs.xsd for more information.
class="com.android.unit_tests.activity.ActivityTests" class="com.android.unit_tests.activity.ActivityTests"
coverage_target="framework" /> coverage_target="framework" />
<test name="vpntests"
build_path="frameworks/base/tests/AndroidTests"
package="com.android.unit_tests"
class="com.android.unit_tests.VpnTest"
coverage_target="framework"
continuous="true" />
<!-- obsolete? <!-- obsolete?
<test name="deadlock" <test name="deadlock"
build_path="frameworks/base/tests/Deadlock" build_path="frameworks/base/tests/Deadlock"
@@ -298,11 +305,11 @@ See test_defs.xsd for more information.
coverage_target="Calendar" coverage_target="Calendar"
continuous="true" /> continuous="true" />
<!-- Make continuous = "true" once the bug 1966269 is fixed -->
<test name="calprov" <test name="calprov"
build_path="packages/providers/CalendarProvider/tests" build_path="packages/providers/CalendarProvider/tests"
package="com.android.providers.calendar.tests" package="com.android.providers.calendar.tests"
coverage_target="CalendarProvider" coverage_target="CalendarProvider" />
continuous="true" />
<test name="camerastress" <test name="camerastress"
build_path="packages/apps/Camera" build_path="packages/apps/Camera"
@@ -460,10 +467,10 @@ See test_defs.xsd for more information.
--> -->
<!-- host java tests --> <!-- host java tests -->
<test-host name="cts-appinstall" <test-host name="cts-appsecurity"
build_path="cts/tests/install-tests" build_path="cts/tests/appsecurity-tests"
class="com.android.cts.install.InstallTests" class="com.android.cts.appsecurity.AppSecurityTests"
jar_name="CtsInstallTests.jar" jar_name="CtsAppSecurityTests.jar"
cts="true" /> cts="true" />
</test-definitions> </test-definitions>

View File

@@ -18,6 +18,7 @@ package com.android.ant;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog; import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager; import com.android.sdklib.SdkManager;
import com.android.sdklib.IAndroidTarget.IOptionalLibrary; import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdklib.internal.project.ProjectProperties;
@@ -27,10 +28,20 @@ import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.ImportTask; import org.apache.tools.ant.taskdefs.ImportTask;
import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Path.PathElement; import org.apache.tools.ant.types.Path.PathElement;
import org.xml.sax.InputSource;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
/** /**
* Setup/Import Ant task. This task accomplishes: * Setup/Import Ant task. This task accomplishes:
@@ -44,13 +55,13 @@ import java.util.HashSet;
* <li>Imports the build rules located in the resolved target so that the build actually does * <li>Imports the build rules located in the resolved target so that the build actually does
* something. This can be disabled with the attribute <var>import</var> set to <code>false</code> * something. This can be disabled with the attribute <var>import</var> set to <code>false</code>
* </li></ul> * </li></ul>
* *
* This is used in build.xml/template. * This is used in build.xml/template.
* *
*/ */
public final class SetupTask extends ImportTask { public final class SetupTask extends ImportTask {
private final static String ANDROID_RULES = "android_rules.xml"; private final static String ANDROID_RULES = "android_rules.xml";
// ant property with the path to the android.jar // ant property with the path to the android.jar
private final static String PROPERTY_ANDROID_JAR = "android-jar"; private final static String PROPERTY_ANDROID_JAR = "android-jar";
// ant property with the path to the framework.jar // ant property with the path to the framework.jar
@@ -63,21 +74,21 @@ public final class SetupTask extends ImportTask {
private final static String PROPERTY_DX = "dx"; private final static String PROPERTY_DX = "dx";
// ref id to the <path> object containing all the boot classpaths. // ref id to the <path> object containing all the boot classpaths.
private final static String REF_CLASSPATH = "android.target.classpath"; private final static String REF_CLASSPATH = "android.target.classpath";
private boolean mDoImport = true; private boolean mDoImport = true;
@Override @Override
public void execute() throws BuildException { public void execute() throws BuildException {
Project antProject = getProject(); Project antProject = getProject();
// get the SDK location // get the SDK location
String sdkLocation = antProject.getProperty(ProjectProperties.PROPERTY_SDK); String sdkLocation = antProject.getProperty(ProjectProperties.PROPERTY_SDK);
// check if it's valid and exists // check if it's valid and exists
if (sdkLocation == null || sdkLocation.length() == 0) { if (sdkLocation == null || sdkLocation.length() == 0) {
throw new BuildException("SDK Location is not set."); throw new BuildException("SDK Location is not set.");
} }
File sdk = new File(sdkLocation); File sdk = new File(sdkLocation);
if (sdk.isDirectory() == false) { if (sdk.isDirectory() == false) {
throw new BuildException(String.format("SDK Location '%s' is not valid.", sdkLocation)); throw new BuildException(String.format("SDK Location '%s' is not valid.", sdkLocation));
@@ -120,20 +131,26 @@ public final class SetupTask extends ImportTask {
// resolve it // resolve it
IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString); IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString);
if (androidTarget == null) { if (androidTarget == null) {
throw new BuildException(String.format( throw new BuildException(String.format(
"Unable to resolve target '%s'", targetHashString)); "Unable to resolve target '%s'", targetHashString));
} }
// display it // display it
System.out.println("Project Target: " + androidTarget.getName()); System.out.println("Project Target: " + androidTarget.getName());
if (androidTarget.isPlatform() == false) { if (androidTarget.isPlatform() == false) {
System.out.println("Vendor: " + androidTarget.getVendor()); System.out.println("Vendor: " + androidTarget.getVendor());
System.out.println("Platform Version: " + androidTarget.getApiVersionName()); System.out.println("Platform Version: " + androidTarget.getVersionName());
} }
System.out.println("API level: " + androidTarget.getApiVersionNumber()); System.out.println("API level: " + androidTarget.getVersion().getApiString());
// if needed check the manifest so that it matches the target
if (androidTarget.getVersion().isPreview()) {
// for preview, the manifest minSdkVersion node *must* match the target codename
checkManifest(antProject, androidTarget.getVersion().getCodename());
}
// sets up the properties to find android.jar/framework.aidl/target tools // sets up the properties to find android.jar/framework.aidl/target tools
String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR); String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR);
antProject.setProperty(PROPERTY_ANDROID_JAR, androidJar); antProject.setProperty(PROPERTY_ANDROID_JAR, androidJar);
@@ -152,7 +169,7 @@ public final class SetupTask extends ImportTask {
// create a PathElement for the framework jar // create a PathElement for the framework jar
PathElement element = bootclasspath.createPathElement(); PathElement element = bootclasspath.createPathElement();
element.setPath(androidJar); element.setPath(androidJar);
// create PathElement for each optional library. // create PathElement for each optional library.
IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries(); IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
if (libraries != null) { if (libraries != null) {
@@ -167,13 +184,13 @@ public final class SetupTask extends ImportTask {
} }
} }
} }
// finally sets the path in the project with a reference // finally sets the path in the project with a reference
antProject.addReference(REF_CLASSPATH, bootclasspath); antProject.addReference(REF_CLASSPATH, bootclasspath);
// find the file to import, and import it. // find the file to import, and import it.
String templateFolder = androidTarget.getPath(IAndroidTarget.TEMPLATES); String templateFolder = androidTarget.getPath(IAndroidTarget.TEMPLATES);
// Now the import section. This is only executed if the task actually has to import a file. // Now the import section. This is only executed if the task actually has to import a file.
if (mDoImport) { if (mDoImport) {
// make sure the file exists. // make sure the file exists.
@@ -182,17 +199,17 @@ public final class SetupTask extends ImportTask {
throw new BuildException(String.format("Template directory '%s' is missing.", throw new BuildException(String.format("Template directory '%s' is missing.",
templateFolder)); templateFolder));
} }
// now check the rules file exists. // now check the rules file exists.
File rules = new File(templateFolder, ANDROID_RULES); File rules = new File(templateFolder, ANDROID_RULES);
if (rules.isFile() == false) { if (rules.isFile() == false) {
throw new BuildException(String.format("Build rules file '%s' is missing.", throw new BuildException(String.format("Build rules file '%s' is missing.",
templateFolder)); templateFolder));
} }
// set the file location to import // set the file location to import
setFile(rules.getAbsolutePath()); setFile(rules.getAbsolutePath());
// and import // and import
super.execute(); super.execute();
} }
@@ -205,4 +222,47 @@ public final class SetupTask extends ImportTask {
public void setImport(boolean value) { public void setImport(boolean value) {
mDoImport = value; mDoImport = value;
} }
private void checkManifest(Project antProject, String codename) {
try {
File manifest = new File(antProject.getBaseDir(), "AndroidManifest.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new NamespaceContext() {
public String getNamespaceURI(String prefix) {
if (prefix != null) {
if (prefix.equals("android")) {
return SdkConstants.NS_RESOURCES;
}
}
return XMLConstants.NULL_NS_URI;
}
public String getPrefix(String namespaceURI) {
// This isn't necessary for our use.
assert false;
return null;
}
public Iterator getPrefixes(String namespaceURI) {
// This isn't necessary for our use.
assert false;
return null;
}
});
String value = xPath.evaluate("/manifest/uses-sdk/@android:minSdkVersion",
new InputSource(new FileInputStream(manifest)));
if (codename.equals(value) == false) {
throw new BuildException(String.format("For '%1$s' SDK Preview, application manifest must declare minSdkVersion to '%1$s'",
codename));
}
} catch (XPathExpressionException e) {
throw new BuildException(e);
} catch (FileNotFoundException e) {
throw new BuildException(e);
}
}
} }

View File

@@ -28,8 +28,11 @@ import java.util.Map;
public interface IDevice { public interface IDevice {
public final static String PROP_BUILD_VERSION = "ro.build.version.release"; public final static String PROP_BUILD_VERSION = "ro.build.version.release";
public final static String PROP_BUILD_VERSION_NUMBER = "ro.build.version.sdk"; public final static String PROP_BUILD_API_LEVEL = "ro.build.version.sdk";
public final static String PROP_BUILD_CODENAME = "ro.build.version.codename";
public final static String PROP_DEBUGGABLE = "ro.debuggable"; public final static String PROP_DEBUGGABLE = "ro.debuggable";
/** Serial number of the first connected emulator. */ /** Serial number of the first connected emulator. */
public final static String FIRST_EMULATOR_SN = "emulator-5554"; //$NON-NLS-1$ public final static String FIRST_EMULATOR_SN = "emulator-5554"; //$NON-NLS-1$
/** Device change bit mask: {@link DeviceState} change. */ /** Device change bit mask: {@link DeviceState} change. */
@@ -39,6 +42,9 @@ public interface IDevice {
/** Device change bit mask: build info change. */ /** Device change bit mask: build info change. */
public static final int CHANGE_BUILD_INFO = 0x0004; public static final int CHANGE_BUILD_INFO = 0x0004;
/** @deprecated Use {@link #PROP_BUILD_API_LEVEL}. */
public final static String PROP_BUILD_VERSION_NUMBER = PROP_BUILD_API_LEVEL;
/** /**
* The state of a device. * The state of a device.
*/ */

View File

@@ -666,6 +666,12 @@
id="com.android.ide.eclipse.adt.refactoring.extract.string" id="com.android.ide.eclipse.adt.refactoring.extract.string"
name="Extract Android String"> name="Extract Android String">
</command> </command>
<keyBinding
commandId="com.android.ide.eclipse.adt.refactoring.extract.string"
contextId="org.eclipse.ui.globalScope"
keyConfigurationId="org.eclipse.ui.defaultAcceleratorConfiguration"
keySequence="M3+M2+A S">
</keyBinding>
</extension> </extension>
<extension <extension
point="org.eclipse.ltk.core.refactoring.refactoringContributions"> point="org.eclipse.ltk.core.refactoring.refactoringContributions">

View File

@@ -24,6 +24,7 @@ import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.project.FixLaunchConfig; import com.android.ide.eclipse.adt.internal.project.FixLaunchConfig;
import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.BasicXmlErrorListener; import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.BasicXmlErrorListener;
import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkConstants;
@@ -89,22 +90,22 @@ public class PreCompilerBuilder extends BaseBuilder {
this.sourceFolder = sourceFolder; this.sourceFolder = sourceFolder;
this.aidlFile = aidlFile; this.aidlFile = aidlFile;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) { if (this == obj) {
return true; return true;
} }
if (obj instanceof AidlData) { if (obj instanceof AidlData) {
AidlData file = (AidlData)obj; AidlData file = (AidlData)obj;
return aidlFile.equals(file.aidlFile) && sourceFolder.equals(file.sourceFolder); return aidlFile.equals(file.aidlFile) && sourceFolder.equals(file.sourceFolder);
} }
return false; return false;
} }
} }
/** /**
* Resource Compile flag. This flag is reset to false after each successful compilation, and * Resource Compile flag. This flag is reset to false after each successful compilation, and
* stored in the project persistent properties. This allows the builder to remember its state * stored in the project persistent properties. This allows the builder to remember its state
@@ -120,7 +121,7 @@ public class PreCompilerBuilder extends BaseBuilder {
/** cache of the java package defined in the manifest */ /** cache of the java package defined in the manifest */
private String mManifestPackage; private String mManifestPackage;
/** Output folder for generated Java File. Created on the Builder init /** Output folder for generated Java File. Created on the Builder init
* @see #startupOnInitialize() * @see #startupOnInitialize()
*/ */
@@ -145,11 +146,11 @@ public class PreCompilerBuilder extends BaseBuilder {
private boolean mDone = false; private boolean mDone = false;
public DerivedProgressMonitor() { public DerivedProgressMonitor() {
} }
void addFile(IFile file) { void addFile(IFile file) {
mFileList.add(file); mFileList.add(file);
} }
void reset() { void reset() {
mFileList.clear(); mFileList.clear();
mDone = false; mDone = false;
@@ -198,7 +199,7 @@ public class PreCompilerBuilder extends BaseBuilder {
public PreCompilerBuilder() { public PreCompilerBuilder() {
super(); super();
} }
// build() returns a list of project from which this project depends for future compilation. // build() returns a list of project from which this project depends for future compilation.
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@@ -209,24 +210,24 @@ public class PreCompilerBuilder extends BaseBuilder {
// First thing we do is go through the resource delta to not // First thing we do is go through the resource delta to not
// lose it if we have to abort the build for any reason. // lose it if we have to abort the build for any reason.
// get the project objects // get the project objects
IProject project = getProject(); IProject project = getProject();
// Top level check to make sure the build can move forward. // Top level check to make sure the build can move forward.
abortOnBadSetup(project); abortOnBadSetup(project);
IJavaProject javaProject = JavaCore.create(project); IJavaProject javaProject = JavaCore.create(project);
IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
// now we need to get the classpath list // now we need to get the classpath list
ArrayList<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths( ArrayList<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths(
javaProject); javaProject);
PreCompilerDeltaVisitor dv = null; PreCompilerDeltaVisitor dv = null;
String javaPackage = null; String javaPackage = null;
int minSdkVersion = AndroidManifestParser.INVALID_MIN_SDK; String minSdkVersion = null;
if (kind == FULL_BUILD) { if (kind == FULL_BUILD) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Start_Full_Pre_Compiler); Messages.Start_Full_Pre_Compiler);
@@ -235,7 +236,7 @@ public class PreCompilerBuilder extends BaseBuilder {
} else { } else {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Start_Inc_Pre_Compiler); Messages.Start_Inc_Pre_Compiler);
// Go through the resources and see if something changed. // Go through the resources and see if something changed.
// Even if the mCompileResources flag is true from a previously aborted // Even if the mCompileResources flag is true from a previously aborted
// build, we need to go through the Resource delta to get a possible // build, we need to go through the Resource delta to get a possible
@@ -247,10 +248,10 @@ public class PreCompilerBuilder extends BaseBuilder {
} else { } else {
dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList); dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList);
delta.accept(dv); delta.accept(dv);
// record the state // record the state
mMustCompileResources |= dv.getCompileResources(); mMustCompileResources |= dv.getCompileResources();
if (dv.getForceAidlCompile()) { if (dv.getForceAidlCompile()) {
buildAidlCompilationList(project, sourceFolderPathList); buildAidlCompilationList(project, sourceFolderPathList);
} else { } else {
@@ -258,46 +259,46 @@ public class PreCompilerBuilder extends BaseBuilder {
mergeAidlFileModifications(dv.getAidlToCompile(), mergeAidlFileModifications(dv.getAidlToCompile(),
dv.getAidlToRemove()); dv.getAidlToRemove());
} }
// get the java package from the visitor // get the java package from the visitor
javaPackage = dv.getManifestPackage(); javaPackage = dv.getManifestPackage();
minSdkVersion = dv.getMinSdkVersion(); minSdkVersion = dv.getMinSdkVersion();
} }
} }
// store the build status in the persistent storage // store the build status in the persistent storage
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mMustCompileResources); saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mMustCompileResources);
// if there was some XML errors, we just return w/o doing // if there was some XML errors, we just return w/o doing
// anything since we've put some markers in the files anyway. // anything since we've put some markers in the files anyway.
if (dv != null && dv.mXmlError) { if (dv != null && dv.mXmlError) {
AdtPlugin.printErrorToConsole(project, Messages.Xml_Error); AdtPlugin.printErrorToConsole(project, Messages.Xml_Error);
// This interrupts the build. The next builders will not run. // This interrupts the build. The next builders will not run.
stopBuild(Messages.Xml_Error); stopBuild(Messages.Xml_Error);
} }
// get the manifest file // get the manifest file
IFile manifest = AndroidManifestParser.getManifest(project); IFile manifest = AndroidManifestParser.getManifest(project);
if (manifest == null) { if (manifest == null) {
String msg = String.format(Messages.s_File_Missing, String msg = String.format(Messages.s_File_Missing,
AndroidConstants.FN_ANDROID_MANIFEST); AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printErrorToConsole(project, msg); AdtPlugin.printErrorToConsole(project, msg);
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// This interrupts the build. The next builders will not run. // This interrupts the build. The next builders will not run.
stopBuild(msg); stopBuild(msg);
} }
// lets check the XML of the manifest first, if that hasn't been done by the // lets check the XML of the manifest first, if that hasn't been done by the
// resource delta visitor yet. // resource delta visitor yet.
if (dv == null || dv.getCheckedManifestXml() == false) { if (dv == null || dv.getCheckedManifestXml() == false) {
BasicXmlErrorListener errorListener = new BasicXmlErrorListener(); BasicXmlErrorListener errorListener = new BasicXmlErrorListener();
AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(manifest, AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(manifest,
errorListener); errorListener);
if (errorListener.mHasXmlError == true) { if (errorListener.mHasXmlError == true) {
// there was an error in the manifest, its file has been marked, // there was an error in the manifest, its file has been marked,
// by the XmlErrorHandler. // by the XmlErrorHandler.
@@ -305,25 +306,71 @@ public class PreCompilerBuilder extends BaseBuilder {
String msg = String.format(Messages.s_Contains_Xml_Error, String msg = String.format(Messages.s_Contains_Xml_Error,
AndroidConstants.FN_ANDROID_MANIFEST); AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
// This interrupts the build. The next builders will not run. // This interrupts the build. The next builders will not run.
stopBuild(msg); stopBuild(msg);
} }
// get the java package from the parser // get the java package from the parser
javaPackage = parser.getPackage(); javaPackage = parser.getPackage();
minSdkVersion = parser.getApiLevelRequirement(); minSdkVersion = parser.getApiLevelRequirement();
} }
if (minSdkVersion != AndroidManifestParser.INVALID_MIN_SDK && if (minSdkVersion != null) {
minSdkVersion < projectTarget.getApiVersionNumber()) { int minSdkValue = -1;
// check it against the target api level try {
String msg = String.format( minSdkValue = Integer.parseInt(minSdkVersion);
"Manifest min SDK version (%1$d) is lower than project target API level (%2$d)", } catch (NumberFormatException e) {
minSdkVersion, projectTarget.getApiVersionNumber()); // it's ok, it means minSdkVersion contains a (hopefully) valid codename.
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); }
BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
IMarker.SEVERITY_WARNING); AndroidVersion projectVersion = projectTarget.getVersion();
if (minSdkValue != -1) {
String codename = projectVersion.getCodename();
if (codename != null) {
// integer minSdk when the target is a preview => fatal error
String msg = String.format(
"Platform %1$s is a preview and requires appication manifests to set %2$s to '%3$s'",
codename, AndroidManifestParser.ATTRIBUTE_MIN_SDK_VERSION,
codename);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
IMarker.SEVERITY_ERROR);
stopBuild(msg);
} else if (minSdkValue < projectVersion.getApiLevel()) {
// integer minSdk is not high enough for the target => warning
String msg = String.format(
"Manifest min SDK version (%1$d) is lower than project target API level (%2$d)",
minSdkVersion, projectVersion.getApiLevel());
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
IMarker.SEVERITY_WARNING);
}
} else {
// looks like the min sdk is a codename, check it matches the codename
// of the platform
String codename = projectVersion.getCodename();
if (codename == null) {
// platform is not a preview => fatal error
String msg = String.format(
"Manifest attribute '%1$s' is set to '%2$s'. Integer is expected.",
AndroidManifestParser.ATTRIBUTE_MIN_SDK_VERSION, codename);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
IMarker.SEVERITY_ERROR);
stopBuild(msg);
} else if (codename.equals(minSdkVersion) == false) {
// platform and manifest codenames don't match => fatal error.
String msg = String.format(
"Value of manifest attribute '%1$s' does not match platform codename '%2$s'",
AndroidManifestParser.ATTRIBUTE_MIN_SDK_VERSION, codename);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
IMarker.SEVERITY_ERROR);
stopBuild(msg);
}
}
} }
if (javaPackage == null || javaPackage.length() == 0) { if (javaPackage == null || javaPackage.length() == 0) {
@@ -332,11 +379,11 @@ public class PreCompilerBuilder extends BaseBuilder {
AndroidConstants.FN_ANDROID_MANIFEST); AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printErrorToConsole(project, msg); AdtPlugin.printErrorToConsole(project, msg);
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// This interrupts the build. The next builders will not run. // This interrupts the build. The next builders will not run.
stopBuild(msg); stopBuild(msg);
} }
// at this point we have the java package. We need to make sure it's not a different // at this point we have the java package. We need to make sure it's not a different
// package than the previous one that were built. // package than the previous one that were built.
if (javaPackage.equals(mManifestPackage) == false) { if (javaPackage.equals(mManifestPackage) == false) {
@@ -345,64 +392,64 @@ public class PreCompilerBuilder extends BaseBuilder {
if (mManifestPackage != null) { if (mManifestPackage != null) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Checking_Package_Change); Messages.Checking_Package_Change);
FixLaunchConfig flc = new FixLaunchConfig(project, mManifestPackage, FixLaunchConfig flc = new FixLaunchConfig(project, mManifestPackage,
javaPackage); javaPackage);
flc.start(); flc.start();
} }
// now we delete the generated classes from their previous location // now we delete the generated classes from their previous location
deleteObsoleteGeneratedClass(AndroidConstants.FN_RESOURCE_CLASS, deleteObsoleteGeneratedClass(AndroidConstants.FN_RESOURCE_CLASS,
mManifestPackage); mManifestPackage);
deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS, deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS,
mManifestPackage); mManifestPackage);
// record the new manifest package, and save it. // record the new manifest package, and save it.
mManifestPackage = javaPackage; mManifestPackage = javaPackage;
saveProjectStringProperty(PROPERTY_PACKAGE, mManifestPackage); saveProjectStringProperty(PROPERTY_PACKAGE, mManifestPackage);
} }
if (mMustCompileResources) { if (mMustCompileResources) {
// we need to figure out where to store the R class. // we need to figure out where to store the R class.
// get the parent folder for R.java and update mManifestPackageSourceFolder // get the parent folder for R.java and update mManifestPackageSourceFolder
IFolder packageFolder = getGenManifestPackageFolder(project); IFolder packageFolder = getGenManifestPackageFolder(project);
// get the resource folder // get the resource folder
IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES); IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES);
// get the file system path // get the file system path
IPath outputLocation = mGenFolder.getLocation(); IPath outputLocation = mGenFolder.getLocation();
IPath resLocation = resFolder.getLocation(); IPath resLocation = resFolder.getLocation();
IPath manifestLocation = manifest.getLocation(); IPath manifestLocation = manifest.getLocation();
// those locations have to exist for us to do something! // those locations have to exist for us to do something!
if (outputLocation != null && resLocation != null if (outputLocation != null && resLocation != null
&& manifestLocation != null) { && manifestLocation != null) {
String osOutputPath = outputLocation.toOSString(); String osOutputPath = outputLocation.toOSString();
String osResPath = resLocation.toOSString(); String osResPath = resLocation.toOSString();
String osManifestPath = manifestLocation.toOSString(); String osManifestPath = manifestLocation.toOSString();
// remove the aapt markers // remove the aapt markers
removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT_COMPILE); removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT_COMPILE);
removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT_COMPILE); removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT_COMPILE);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Preparing_Generated_Files); Messages.Preparing_Generated_Files);
// since the R.java file may be already existing in read-only // since the R.java file may be already existing in read-only
// mode we need to make it readable so that aapt can overwrite // mode we need to make it readable so that aapt can overwrite
// it // it
IFile rJavaFile = packageFolder.getFile(AndroidConstants.FN_RESOURCE_CLASS); IFile rJavaFile = packageFolder.getFile(AndroidConstants.FN_RESOURCE_CLASS);
// do the same for the Manifest.java class // do the same for the Manifest.java class
IFile manifestJavaFile = packageFolder.getFile( IFile manifestJavaFile = packageFolder.getFile(
AndroidConstants.FN_MANIFEST_CLASS); AndroidConstants.FN_MANIFEST_CLASS);
// we actually need to delete the manifest.java as it may become empty and // we actually need to delete the manifest.java as it may become empty and
// in this case aapt doesn't generate an empty one, but instead doesn't // in this case aapt doesn't generate an empty one, but instead doesn't
// touch it. // touch it.
manifestJavaFile.delete(true, null); manifestJavaFile.delete(true, null);
// launch aapt: create the command line // launch aapt: create the command line
ArrayList<String> array = new ArrayList<String>(); ArrayList<String> array = new ArrayList<String>();
array.add(projectTarget.getPath(IAndroidTarget.AAPT)); array.add(projectTarget.getPath(IAndroidTarget.AAPT));
@@ -419,7 +466,7 @@ public class PreCompilerBuilder extends BaseBuilder {
array.add(osResPath); array.add(osResPath);
array.add("-I"); //$NON-NLS-1$ array.add("-I"); //$NON-NLS-1$
array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR)); array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR));
if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) { if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (String c : array) { for (String c : array) {
@@ -429,23 +476,23 @@ public class PreCompilerBuilder extends BaseBuilder {
String cmd_line = sb.toString(); String cmd_line = sb.toString();
AdtPlugin.printToConsole(project, cmd_line); AdtPlugin.printToConsole(project, cmd_line);
} }
// launch // launch
int execError = 1; int execError = 1;
try { try {
// launch the command line process // launch the command line process
Process process = Runtime.getRuntime().exec( Process process = Runtime.getRuntime().exec(
array.toArray(new String[array.size()])); array.toArray(new String[array.size()]));
// list to store each line of stderr // list to store each line of stderr
ArrayList<String> results = new ArrayList<String>(); ArrayList<String> results = new ArrayList<String>();
// get the output and return code from the process // get the output and return code from the process
execError = grabProcessOutput(process, results); execError = grabProcessOutput(process, results);
// attempt to parse the error output // attempt to parse the error output
boolean parsingError = parseAaptOutput(results, project); boolean parsingError = parseAaptOutput(results, project);
// if we couldn't parse the output we display it in the console. // if we couldn't parse the output we display it in the console.
if (parsingError) { if (parsingError) {
if (execError != 0) { if (execError != 0) {
@@ -455,7 +502,7 @@ public class PreCompilerBuilder extends BaseBuilder {
project, results.toArray()); project, results.toArray());
} }
} }
if (execError != 0) { if (execError != 0) {
// if the exec failed, and we couldn't parse the error output // if the exec failed, and we couldn't parse the error output
// (and therefore not all files that should have been marked, // (and therefore not all files that should have been marked,
@@ -464,10 +511,10 @@ public class PreCompilerBuilder extends BaseBuilder {
markProject(AdtConstants.MARKER_ADT, Messages.Unparsed_AAPT_Errors, markProject(AdtConstants.MARKER_ADT, Messages.Unparsed_AAPT_Errors,
IMarker.SEVERITY_ERROR); IMarker.SEVERITY_ERROR);
} }
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.AAPT_Error); Messages.AAPT_Error);
// abort if exec failed. // abort if exec failed.
// This interrupts the build. The next builders will not run. // This interrupts the build. The next builders will not run.
stopBuild(Messages.AAPT_Error); stopBuild(Messages.AAPT_Error);
@@ -477,7 +524,7 @@ public class PreCompilerBuilder extends BaseBuilder {
// mark the project and exit // mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); String msg = String.format(Messages.AAPT_Exec_Error, array.get(0));
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// This interrupts the build. The next builders will not run. // This interrupts the build. The next builders will not run.
stopBuild(msg); stopBuild(msg);
} catch (InterruptedException e) { } catch (InterruptedException e) {
@@ -485,11 +532,11 @@ public class PreCompilerBuilder extends BaseBuilder {
// mark the project and exit // mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); String msg = String.format(Messages.AAPT_Exec_Error, array.get(0));
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// This interrupts the build. The next builders will not run. // This interrupts the build. The next builders will not run.
stopBuild(msg); stopBuild(msg);
} }
// if the return code was OK, we refresh the folder that // if the return code was OK, we refresh the folder that
// contains R.java to force a java recompile. // contains R.java to force a java recompile.
if (execError == 0) { if (execError == 0) {
@@ -497,10 +544,10 @@ public class PreCompilerBuilder extends BaseBuilder {
// as derived. // as derived.
mDerivedProgressMonitor.addFile(rJavaFile); mDerivedProgressMonitor.addFile(rJavaFile);
mDerivedProgressMonitor.addFile(manifestJavaFile); mDerivedProgressMonitor.addFile(manifestJavaFile);
// build has been done. reset the state of the builder // build has been done. reset the state of the builder
mMustCompileResources = false; mMustCompileResources = false;
// and store it // and store it
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES,
mMustCompileResources); mMustCompileResources);
@@ -509,10 +556,10 @@ public class PreCompilerBuilder extends BaseBuilder {
} else { } else {
// nothing to do // nothing to do
} }
// now handle the aidl stuff. // now handle the aidl stuff.
boolean aidlStatus = handleAidl(projectTarget, sourceFolderPathList, monitor); boolean aidlStatus = handleAidl(projectTarget, sourceFolderPathList, monitor);
if (aidlStatus == false && mMustCompileResources == false) { if (aidlStatus == false && mMustCompileResources == false) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Nothing_To_Compile); Messages.Nothing_To_Compile);
@@ -540,14 +587,14 @@ public class PreCompilerBuilder extends BaseBuilder {
@Override @Override
protected void startupOnInitialize() { protected void startupOnInitialize() {
super.startupOnInitialize(); super.startupOnInitialize();
mDerivedProgressMonitor = new DerivedProgressMonitor(); mDerivedProgressMonitor = new DerivedProgressMonitor();
IProject project = getProject(); IProject project = getProject();
// load the previous IFolder and java package. // load the previous IFolder and java package.
mManifestPackage = loadProjectStringProperty(PROPERTY_PACKAGE); mManifestPackage = loadProjectStringProperty(PROPERTY_PACKAGE);
// get the source folder in which all the Java files are created // get the source folder in which all the Java files are created
mGenFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES); mGenFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES);
@@ -555,14 +602,14 @@ public class PreCompilerBuilder extends BaseBuilder {
// recompile. // recompile.
mMustCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true); mMustCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true);
boolean mustCompileAidl = loadProjectBooleanProperty(PROPERTY_COMPILE_AIDL, true); boolean mustCompileAidl = loadProjectBooleanProperty(PROPERTY_COMPILE_AIDL, true);
// if we stored that we have to compile some aidl, we build the list that will compile them // if we stored that we have to compile some aidl, we build the list that will compile them
// all // all
if (mustCompileAidl) { if (mustCompileAidl) {
IJavaProject javaProject = JavaCore.create(project); IJavaProject javaProject = JavaCore.create(project);
ArrayList<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths( ArrayList<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths(
javaProject); javaProject);
buildAidlCompilationList(project, sourceFolderPathList); buildAidlCompilationList(project, sourceFolderPathList);
} }
} }
@@ -576,7 +623,7 @@ public class PreCompilerBuilder extends BaseBuilder {
if (javaPackage == null) { if (javaPackage == null) {
return; return;
} }
IPath packagePath = getJavaPackagePath(javaPackage); IPath packagePath = getJavaPackagePath(javaPackage);
IPath iPath = packagePath.append(filename); IPath iPath = packagePath.append(filename);
@@ -614,10 +661,10 @@ public class PreCompilerBuilder extends BaseBuilder {
path.append(AndroidConstants.WS_SEP_CHAR); path.append(AndroidConstants.WS_SEP_CHAR);
path.append(s); path.append(s);
} }
return new Path(path.toString()); return new Path(path.toString());
} }
/** /**
* Returns an {@link IFolder} (located inside the 'gen' source folder), that matches the * Returns an {@link IFolder} (located inside the 'gen' source folder), that matches the
* package defined in the manifest. This {@link IFolder} may not actually exist * package defined in the manifest. This {@link IFolder} may not actually exist
@@ -630,7 +677,7 @@ public class PreCompilerBuilder extends BaseBuilder {
throws CoreException { throws CoreException {
// get the path for the package // get the path for the package
IPath packagePath = getJavaPackagePath(mManifestPackage); IPath packagePath = getJavaPackagePath(mManifestPackage);
// get a folder for this path under the 'gen' source folder, and return it. // get a folder for this path under the 'gen' source folder, and return it.
// This IFolder may not reference an actual existing folder. // This IFolder may not reference an actual existing folder.
return mGenFolder.getFolder(packagePath); return mGenFolder.getFolder(packagePath);
@@ -657,10 +704,10 @@ public class PreCompilerBuilder extends BaseBuilder {
command[index++] = projectTarget.getPath(IAndroidTarget.AIDL); command[index++] = projectTarget.getPath(IAndroidTarget.AIDL);
command[index++] = "-p" + Sdk.getCurrent().getTarget(getProject()).getPath( //$NON-NLS-1$ command[index++] = "-p" + Sdk.getCurrent().getTarget(getProject()).getPath( //$NON-NLS-1$
IAndroidTarget.ANDROID_AIDL); IAndroidTarget.ANDROID_AIDL);
// since the path are relative to the workspace and not the project itself, we need // since the path are relative to the workspace and not the project itself, we need
// the workspace root. // the workspace root.
IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
for (IPath p : sourceFolders) { for (IPath p : sourceFolders) {
IFolder f = wsRoot.getFolder(p); IFolder f = wsRoot.getFolder(p);
command[index++] = "-I" + f.getLocation().toOSString(); //$NON-NLS-1$ command[index++] = "-I" + f.getLocation().toOSString(); //$NON-NLS-1$
@@ -686,7 +733,7 @@ public class PreCompilerBuilder extends BaseBuilder {
// get the path of the source file. // get the path of the source file.
IPath sourcePath = aidlData.aidlFile.getLocation(); IPath sourcePath = aidlData.aidlFile.getLocation();
String osSourcePath = sourcePath.toOSString(); String osSourcePath = sourcePath.toOSString();
IFile javaFile = getGenDestinationFile(aidlData, true /*createFolders*/, monitor); IFile javaFile = getGenDestinationFile(aidlData, true /*createFolders*/, monitor);
// finish to set the command line. // finish to set the command line.
@@ -755,15 +802,15 @@ public class PreCompilerBuilder extends BaseBuilder {
IPath packagePath = aidlData.aidlFile.getFullPath().removeFirstSegments( IPath packagePath = aidlData.aidlFile.getFullPath().removeFirstSegments(
segmentToSourceFolderCount).removeLastSegments(1); segmentToSourceFolderCount).removeLastSegments(1);
Path destinationPath = new Path(packagePath.toString()); Path destinationPath = new Path(packagePath.toString());
// get an IFolder for this path. It's relative to the 'gen' folder already // get an IFolder for this path. It's relative to the 'gen' folder already
IFolder destinationFolder = mGenFolder.getFolder(destinationPath); IFolder destinationFolder = mGenFolder.getFolder(destinationPath);
// create it if needed. // create it if needed.
if (destinationFolder.exists() == false && createFolders) { if (destinationFolder.exists() == false && createFolders) {
createFolder(destinationFolder, monitor); createFolder(destinationFolder, monitor);
} }
// Build the Java file name from the aidl name. // Build the Java file name from the aidl name.
String javaName = aidlData.aidlFile.getName().replaceAll(AndroidConstants.RE_AIDL_EXT, String javaName = aidlData.aidlFile.getName().replaceAll(AndroidConstants.RE_AIDL_EXT,
AndroidConstants.DOT_JAVA); AndroidConstants.DOT_JAVA);
@@ -776,15 +823,15 @@ public class PreCompilerBuilder extends BaseBuilder {
/** /**
* Creates the destination folder. Because * Creates the destination folder. Because
* {@link IFolder#create(boolean, boolean, IProgressMonitor)} only works if the parent folder * {@link IFolder#create(boolean, boolean, IProgressMonitor)} only works if the parent folder
* already exists, this goes and ensure that all the parent folders actually exist, or it * already exists, this goes and ensure that all the parent folders actually exist, or it
* creates them as well. * creates them as well.
* @param destinationFolder The folder to create * @param destinationFolder The folder to create
* @param monitor the {@link IProgressMonitor}, * @param monitor the {@link IProgressMonitor},
* @throws CoreException * @throws CoreException
*/ */
private void createFolder(IFolder destinationFolder, IProgressMonitor monitor) private void createFolder(IFolder destinationFolder, IProgressMonitor monitor)
throws CoreException { throws CoreException {
// check the parent exist and create if necessary. // check the parent exist and create if necessary.
IContainer parent = destinationFolder.getParent(); IContainer parent = destinationFolder.getParent();
if (parent.getType() == IResource.FOLDER && parent.exists() == false) { if (parent.getType() == IResource.FOLDER && parent.exists() == false) {

View File

@@ -44,16 +44,16 @@ import java.util.ArrayList;
* {@link PreCompilerBuilder}: * {@link PreCompilerBuilder}:
* <ul><li>R.java/Manifest.java generated by compiling the resources</li> * <ul><li>R.java/Manifest.java generated by compiling the resources</li>
* <li>Any Java files generated by <code>aidl</code></li></ul>. * <li>Any Java files generated by <code>aidl</code></li></ul>.
* *
* Therefore it looks for the following: * Therefore it looks for the following:
* <ul><li>Any modification in the resource folder</li> * <ul><li>Any modification in the resource folder</li>
* <li>Removed files from the source folder receiving generated Java files</li> * <li>Removed files from the source folder receiving generated Java files</li>
* <li>Any modification to aidl files.</li> * <li>Any modification to aidl files.</li>
* *
*/ */
class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
IResourceDeltaVisitor { IResourceDeltaVisitor {
private enum AidlType { private enum AidlType {
UNKNOWN, INTERFACE, PARCELABLE; UNKNOWN, INTERFACE, PARCELABLE;
} }
@@ -73,7 +73,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
* into R.java * into R.java
*/ */
private boolean mCompileResources = false; private boolean mCompileResources = false;
/** /**
* Aidl force recompilation flag. If true, we'll attempt to recompile all aidl files. * Aidl force recompilation flag. If true, we'll attempt to recompile all aidl files.
*/ */
@@ -84,14 +84,14 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
/** List of .aidl files that have been removed. */ /** List of .aidl files that have been removed. */
private final ArrayList<AidlData> mAidlToRemove = new ArrayList<AidlData>(); private final ArrayList<AidlData> mAidlToRemove = new ArrayList<AidlData>();
/** Manifest check/parsing flag. */ /** Manifest check/parsing flag. */
private boolean mCheckedManifestXml = false; private boolean mCheckedManifestXml = false;
/** Application Package, gathered from the parsing of the manifest */ /** Application Package, gathered from the parsing of the manifest */
private String mJavaPackage = null; private String mJavaPackage = null;
/** minSDKVersion attribute value, gathered from the parsing of the manifest */ /** minSDKVersion attribute value, gathered from the parsing of the manifest */
private int mMinSdkVersion = AndroidManifestParser.INVALID_MIN_SDK; private String mMinSdkVersion = null;
// Internal usage fields. // Internal usage fields.
/** /**
@@ -126,7 +126,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
public boolean getForceAidlCompile() { public boolean getForceAidlCompile() {
return mForceAidlCompile; return mForceAidlCompile;
} }
public ArrayList<AidlData> getAidlToCompile() { public ArrayList<AidlData> getAidlToCompile() {
return mAidlToCompile; return mAidlToCompile;
} }
@@ -134,7 +134,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
public ArrayList<AidlData> getAidlToRemove() { public ArrayList<AidlData> getAidlToRemove() {
return mAidlToRemove; return mAidlToRemove;
} }
/** /**
* Returns whether the manifest file was parsed/checked for error during the resource delta * Returns whether the manifest file was parsed/checked for error during the resource delta
* visiting. * visiting.
@@ -142,7 +142,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
public boolean getCheckedManifestXml() { public boolean getCheckedManifestXml() {
return mCheckedManifestXml; return mCheckedManifestXml;
} }
/** /**
* Returns the manifest package if the manifest was checked/parsed. * Returns the manifest package if the manifest was checked/parsed.
* <p/> * <p/>
@@ -162,16 +162,16 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
/** /**
* Returns the minSDkVersion attribute from the manifest if it was checked/parsed. * Returns the minSDkVersion attribute from the manifest if it was checked/parsed.
* <p/> * <p/>
* This can return {@link AndroidManifestParser#INVALID_MIN_SDK} in two cases: * This can return null in two cases:
* <ul> * <ul>
* <li>The manifest was not part of the resource change delta, and the manifest was * <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> * not checked/parsed ({@link #getCheckedManifestXml()} returns <code>false</code>)</li>
* <li>The manifest was parsed ({@link #getCheckedManifestXml()} returns <code>true</code>), * <li>The manifest was parsed ({@link #getCheckedManifestXml()} returns <code>true</code>),
* but the package declaration is missing</li> * but the package declaration is missing</li>
* </ul> * </ul>
* @return the minSdkVersion or {@link AndroidManifestParser#INVALID_MIN_SDK}. * @return the minSdkVersion or null.
*/ */
public int getMinSdkVersion() { public String getMinSdkVersion() {
return mMinSdkVersion; return mMinSdkVersion;
} }
@@ -219,7 +219,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
// parse the manifest for errors // parse the manifest for errors
AndroidManifestParser parser = BaseProjectHelper.parseManifestForError( AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(
(IFile)resource, this); (IFile)resource, this);
if (parser != null) { if (parser != null) {
mJavaPackage = parser.getPackage(); mJavaPackage = parser.getPackage();
mMinSdkVersion = parser.getApiLevelRequirement(); mMinSdkVersion = parser.getApiLevelRequirement();
@@ -287,19 +287,19 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
// Look for the source aidl file in all the source folders. // Look for the source aidl file in all the source folders.
String aidlFileName = fileName.replaceAll(AndroidConstants.RE_JAVA_EXT, String aidlFileName = fileName.replaceAll(AndroidConstants.RE_JAVA_EXT,
AndroidConstants.DOT_AIDL); AndroidConstants.DOT_AIDL);
for (IPath sourceFolderPath : mSourceFolders) { for (IPath sourceFolderPath : mSourceFolders) {
// do not search in the current source folder as it is the 'gen' folder. // do not search in the current source folder as it is the 'gen' folder.
if (sourceFolderPath.equals(mSourceFolder.getFullPath())) { if (sourceFolderPath.equals(mSourceFolder.getFullPath())) {
continue; continue;
} }
IFolder sourceFolder = getFolder(sourceFolderPath); IFolder sourceFolder = getFolder(sourceFolderPath);
if (sourceFolder != null) { if (sourceFolder != null) {
// go recursively, segment by segment. // go recursively, segment by segment.
// index starts at 2 (0 is project, 1 is 'gen' // index starts at 2 (0 is project, 1 is 'gen'
IFile sourceFile = findFile(sourceFolder, segments, 2, aidlFileName); IFile sourceFile = findFile(sourceFolder, segments, 2, aidlFileName);
if (sourceFile != null) { if (sourceFile != null) {
// found the source. add it to the list of files to compile // found the source. add it to the list of files to compile
mAidlToCompile.add(new AidlData(sourceFolder, sourceFile)); mAidlToCompile.add(new AidlData(sourceFolder, sourceFile));
@@ -331,7 +331,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
if (AndroidConstants.EXT_AIDL.equalsIgnoreCase(ext)) { if (AndroidConstants.EXT_AIDL.equalsIgnoreCase(ext)) {
// first check whether it's a regular file or a parcelable. // first check whether it's a regular file or a parcelable.
AidlType type = getAidlType(file); AidlType type = getAidlType(file);
if (type == AidlType.INTERFACE) { if (type == AidlType.INTERFACE) {
if (kind == IResourceDelta.REMOVED) { if (kind == IResourceDelta.REMOVED) {
// we'll have to remove the generated file. // we'll have to remove the generated file.
@@ -423,7 +423,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
path.segment(1).equals(SdkConstants.FD_GEN_SOURCES); path.segment(1).equals(SdkConstants.FD_GEN_SOURCES);
return true; return true;
} }
// check if we are on the way to a source folder. // check if we are on the way to a source folder.
int count = sourceFolderPath.matchingFirstSegments(path); int count = sourceFolderPath.matchingFirstSegments(path);
if (count == path.segmentCount()) { if (count == path.segmentCount()) {
@@ -443,7 +443,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
return false; return false;
} }
/** /**
* Searches for and return a file in a folder. The file is defined by its segments, and a new * Searches for and return a file in a folder. The file is defined by its segments, and a new
* name (replacing the last segment). * name (replacing the last segment).
@@ -482,10 +482,10 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
if (resource != null && resource.exists() && resource.getType() == IResource.FOLDER) { if (resource != null && resource.exists() && resource.getType() == IResource.FOLDER) {
return (IFolder)resource; return (IFolder)resource;
} }
return null; return null;
} }
/** /**
* Returns the type of the aidl file. Aidl files can either declare interfaces, or declare * Returns the type of the aidl file. Aidl files can either declare interfaces, or declare
* parcelables. This method will attempt to parse the file and return the type. If the type * parcelables. This method will attempt to parse the file and return the type. If the type

View File

@@ -310,12 +310,12 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
Object[] choices = null; Object[] choices = null;
if (attrInfo.isInValue) { if (attrInfo.isInValue) {
// Editing an attribute's value... Get the attribute name and then the // Editing an attribute's value... Get the attribute name and then the
// possible choice for the tuple(parent,attribute) // possible choices for the tuple(parent,attribute)
String value = attrInfo.value; String value = attrInfo.value;
if (value.startsWith("'") || value.startsWith("\"")) { //$NON-NLS-1$ //$NON-NLS-2$ if (value.startsWith("'") || value.startsWith("\"")) { //$NON-NLS-1$ //$NON-NLS-2$
value = value.substring(1); value = value.substring(1);
// The prefix that was found at the beginning only scan for characters // The prefix that was found at the beginning only scan for characters
// valid of tag name. We now know the real prefix for this attribute's // valid for tag name. We now know the real prefix for this attribute's
// value, which is needed to generate the completion choices below. // value, which is needed to generate the completion choices below.
attrInfo.correctedPrefix = value; attrInfo.correctedPrefix = value;
} else { } else {
@@ -772,7 +772,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
IDescriptorProvider descriptorProvider = data.getDescriptorProvider(mDescriptorId); IDescriptorProvider descriptorProvider = data.getDescriptorProvider(mDescriptorId);
if (descriptorProvider != null) { if (descriptorProvider != null) {
mRootDescriptor = new ElementDescriptor("", mRootDescriptor = new ElementDescriptor("", //$NON-NLS-1$
descriptorProvider.getRootElementDescriptors()); descriptorProvider.getRootElementDescriptors());
} }
} }

View File

@@ -79,7 +79,7 @@ import java.net.URL;
* source editor. This can be a no-op if desired. * source editor. This can be a no-op if desired.
*/ */
public abstract class AndroidEditor extends FormEditor implements IResourceChangeListener { public abstract class AndroidEditor extends FormEditor implements IResourceChangeListener {
/** Preference name for the current page of this file */ /** Preference name for the current page of this file */
private static final String PREF_CURRENT_PAGE = "_current_page"; private static final String PREF_CURRENT_PAGE = "_current_page";
@@ -91,7 +91,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
/** Width hint for text fields. Helps the grid layout resize properly on smaller screens */ /** Width hint for text fields. Helps the grid layout resize properly on smaller screens */
public static final int TEXT_WIDTH_HINT = 50; public static final int TEXT_WIDTH_HINT = 50;
/** Page index of the text editor (always the last page) */ /** Page index of the text editor (always the last page) */
private int mTextPageIndex; private int mTextPageIndex;
/** The text editor */ /** The text editor */
@@ -108,7 +108,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
public AndroidEditor() { public AndroidEditor() {
super(); super();
ResourcesPlugin.getWorkspace().addResourceChangeListener(this); ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
mTargetListener = new ITargetChangeListener() { mTargetListener = new ITargetChangeListener() {
public void onProjectTargetChange(IProject changedProject) { public void onProjectTargetChange(IProject changedProject) {
if (changedProject == getProject()) { if (changedProject == getProject()) {
@@ -118,7 +118,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
public void onTargetsLoaded() { public void onTargetsLoaded() {
commitPages(false /* onSave */); commitPages(false /* onSave */);
// recreate the ui root node always // recreate the ui root node always
initUiRootNode(true /*force*/); initUiRootNode(true /*force*/);
} }
@@ -133,14 +133,14 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
* UI node editor. * UI node editor.
*/ */
abstract public UiElementNode getUiRootNode(); abstract public UiElementNode getUiRootNode();
/** /**
* Creates the various form pages. * Creates the various form pages.
* <p/> * <p/>
* Derived classes must implement this to add their own specific tabs. * Derived classes must implement this to add their own specific tabs.
*/ */
abstract protected void createFormPages(); abstract protected void createFormPages();
/** /**
* Creates the initial UI Root Node, including the known mandatory elements. * Creates the initial UI Root Node, including the known mandatory elements.
* @param force if true, a new UiManifestNode is recreated even if it already exists. * @param force if true, a new UiManifestNode is recreated even if it already exists.
@@ -150,9 +150,9 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
/** /**
* Subclasses should override this method to process the new XML Model, which XML * Subclasses should override this method to process the new XML Model, which XML
* root node is given. * root node is given.
* *
* The base implementation is empty. * The base implementation is empty.
* *
* @param xml_doc The XML document, if available, or null if none exists. * @param xml_doc The XML document, if available, or null if none exists.
*/ */
protected void xmlModelChanged(Document xml_doc) { protected void xmlModelChanged(Document xml_doc) {
@@ -169,7 +169,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
createAndroidPages(); createAndroidPages();
selectDefaultPage(null /* defaultPageId */); selectDefaultPage(null /* defaultPageId */);
} }
/** /**
* Creates the page for the Android Editors * Creates the page for the Android Editors
*/ */
@@ -193,7 +193,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
action = mTextEditor.getAction(ActionFactory.REDO.getId()); action = mTextEditor.getAction(ActionFactory.REDO.getId());
bars.setGlobalActionHandler(ActionFactory.REDO.getId(), action); bars.setGlobalActionHandler(ActionFactory.REDO.getId(), action);
bars.updateActionBars(); bars.updateActionBars();
} }
} }
@@ -207,7 +207,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
if (defaultPageId == null) { if (defaultPageId == null) {
if (getEditorInput() instanceof IFileEditorInput) { if (getEditorInput() instanceof IFileEditorInput) {
IFile file = ((IFileEditorInput) getEditorInput()).getFile(); IFile file = ((IFileEditorInput) getEditorInput()).getFile();
QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID, QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID,
getClass().getSimpleName() + PREF_CURRENT_PAGE); getClass().getSimpleName() + PREF_CURRENT_PAGE);
String pageId; String pageId;
@@ -234,7 +234,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
} }
} }
/** /**
* Removes all the pages from the editor. * Removes all the pages from the editor.
*/ */
@@ -247,10 +247,10 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
/** /**
* Overrides the parent's setActivePage to be able to switch to the xml editor. * Overrides the parent's setActivePage to be able to switch to the xml editor.
* *
* If the special pageId TEXT_EDITOR_ID is given, switches to the mTextPageIndex page. * If the special pageId TEXT_EDITOR_ID is given, switches to the mTextPageIndex page.
* This is needed because the editor doesn't actually derive from IFormPage and thus * This is needed because the editor doesn't actually derive from IFormPage and thus
* doesn't have the get-by-page-id method. In this case, the method returns null since * doesn't have the get-by-page-id method. In this case, the method returns null since
* IEditorPart does not implement IFormPage. * IEditorPart does not implement IFormPage.
*/ */
@Override @Override
@@ -262,18 +262,18 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
return super.setActivePage(pageId); return super.setActivePage(pageId);
} }
} }
/** /**
* Notifies this multi-page editor that the page with the given id has been * Notifies this multi-page editor that the page with the given id has been
* activated. This method is called when the user selects a different tab. * activated. This method is called when the user selects a different tab.
* *
* @see MultiPageEditorPart#pageChange(int) * @see MultiPageEditorPart#pageChange(int)
*/ */
@Override @Override
protected void pageChange(int newPageIndex) { protected void pageChange(int newPageIndex) {
super.pageChange(newPageIndex); super.pageChange(newPageIndex);
if (getEditorInput() instanceof IFileEditorInput) { if (getEditorInput() instanceof IFileEditorInput) {
IFile file = ((IFileEditorInput) getEditorInput()).getFile(); IFile file = ((IFileEditorInput) getEditorInput()).getFile();
@@ -288,9 +288,9 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
/** /**
* Notifies this listener that some resource changes * Notifies this listener that some resource changes
* are happening, or have already happened. * are happening, or have already happened.
* *
* Closes all project files on project close. * Closes all project files on project close.
* @see IResourceChangeListener * @see IResourceChangeListener
*/ */
@@ -318,7 +318,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
* Initializes the editor part with a site and input. * Initializes the editor part with a site and input.
* <p/> * <p/>
* Checks that the input is an instance of {@link IFileEditorInput}. * Checks that the input is an instance of {@link IFileEditorInput}.
* *
* @see FormEditor * @see FormEditor
*/ */
@Override @Override
@@ -330,7 +330,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
/** /**
* Removes attached listeners. * Removes attached listeners.
* *
* @see WorkbenchPart * @see WorkbenchPart
*/ */
@Override @Override
@@ -341,7 +341,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
if (mXmlModelStateListener != null) { if (mXmlModelStateListener != null) {
xml_model.removeModelStateListener(mXmlModelStateListener); xml_model.removeModelStateListener(mXmlModelStateListener);
} }
} finally { } finally {
xml_model.releaseFromRead(); xml_model.releaseFromRead();
} }
@@ -355,7 +355,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
super.dispose(); super.dispose();
} }
/** /**
* Commit all dirty pages then saves the contents of the text editor. * Commit all dirty pages then saves the contents of the text editor.
* <p/> * <p/>
@@ -403,7 +403,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
* The incorrect casting makes the original implementation crash due * The incorrect casting makes the original implementation crash due
* to our {@link StructuredTextEditor} not being an {@link IFormPage} * to our {@link StructuredTextEditor} not being an {@link IFormPage}
* so we have to override and duplicate to fix it. * so we have to override and duplicate to fix it.
* *
* @param onSave <code>true</code> if commit is performed as part * @param onSave <code>true</code> if commit is performed as part
* of the 'save' operation, <code>false</code> otherwise. * of the 'save' operation, <code>false</code> otherwise.
* @since 3.3 * @since 3.3
@@ -421,7 +421,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
} }
} }
} }
} }
/* (non-Javadoc) /* (non-Javadoc)
@@ -451,8 +451,8 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
* <li> Links starting with "file:/" are simply sent to a local browser. * <li> Links starting with "file:/" are simply sent to a local browser.
* <li> Links starting with "page:" are expected to be an editor page id to switch to. * <li> Links starting with "page:" are expected to be an editor page id to switch to.
* <li> Other links are ignored. * <li> Other links are ignored.
* </ul> * </ul>
* *
* @return A new hyper-link listener for FormText to use. * @return A new hyper-link listener for FormText to use.
*/ */
public final IHyperlinkListener createHyperlinkListener() { public final IHyperlinkListener createHyperlinkListener() {
@@ -478,7 +478,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
/** /**
* Open the http link into a browser * Open the http link into a browser
* *
* @param link The URL to open in a browser * @param link The URL to open in a browser
*/ */
private void openLinkInBrowser(String link) { private void openLinkInBrowser(String link) {
@@ -516,7 +516,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
"Error opening the Android XML editor. Is the document an XML file?"); "Error opening the Android XML editor. Is the document an XML file?");
throw new RuntimeException("Android XML Editor Error", new CoreException(status)); throw new RuntimeException("Android XML Editor Error", new CoreException(status));
} }
IStructuredModel xml_model = getModelForRead(); IStructuredModel xml_model = getModelForRead();
if (xml_model != null) { if (xml_model != null) {
try { try {
@@ -534,9 +534,9 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
"Android XML Editor Error", null, e.getStatus()); "Android XML Editor Error", null, e.getStatus());
} }
} }
/** /**
* Returns the ISourceViewer associated with the Structured Text editor. * Returns the ISourceViewer associated with the Structured Text editor.
*/ */
public final ISourceViewer getStructuredSourceViewer() { public final ISourceViewer getStructuredSourceViewer() {
if (mTextEditor != null) { if (mTextEditor != null) {
@@ -558,13 +558,18 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
return null; return null;
} }
/** /**
* Returns a version of the model that has been shared for read. * Returns a version of the model that has been shared for read.
* <p/> * <p/>
* Callers <em>must</em> call model.releaseFromRead() when done, typically * Callers <em>must</em> call model.releaseFromRead() when done, typically
* in a try..finally clause. * in a try..finally clause.
* *
* Portability note: this uses getModelManager which is part of wst.sse.core; however
* the interface returned is part of wst.sse.core.internal.provisional so we can
* expect it to change in a distant future if they start cleaning their codebase,
* however unlikely that is.
*
* @return The model for the XML document or null if cannot be obtained from the editor * @return The model for the XML document or null if cannot be obtained from the editor
*/ */
public final IStructuredModel getModelForRead() { public final IStructuredModel getModelForRead() {
@@ -576,14 +581,14 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
} }
return null; return null;
} }
/** /**
* Returns a version of the model that has been shared for edit. * Returns a version of the model that has been shared for edit.
* <p/> * <p/>
* Callers <em>must</em> call model.releaseFromEdit() when done, typically * Callers <em>must</em> call model.releaseFromEdit() when done, typically
* in a try..finally clause. * in a try..finally clause.
* *
* @return The model for the XML document or null if cannot be obtained from the editor * @return The model for the XML document or null if cannot be obtained from the editor
*/ */
public final IStructuredModel getModelForEdit() { public final IStructuredModel getModelForEdit() {
@@ -595,7 +600,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
} }
return null; return null;
} }
/** /**
* Helper class to perform edits on the XML model whilst making sure the * Helper class to perform edits on the XML model whilst making sure the
@@ -609,7 +614,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
* <p/> * <p/>
* The method is synchronous. As soon as the {@link IStructuredModel#changedModel()} method * The method is synchronous. As soon as the {@link IStructuredModel#changedModel()} method
* is called, XML model listeners will be triggered. * is called, XML model listeners will be triggered.
* *
* @param edit_action Something that will change the XML. * @param edit_action Something that will change the XML.
*/ */
public final void editXmlModel(Runnable edit_action) { public final void editXmlModel(Runnable edit_action) {
@@ -623,7 +628,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
model.releaseFromEdit(); model.releaseFromEdit();
} }
} }
/** /**
* Starts an "undo recording" session. This is managed by the underlying undo manager * Starts an "undo recording" session. This is managed by the underlying undo manager
* associated to the structured XML model. * associated to the structured XML model.
@@ -632,7 +637,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
* <p/> * <p/>
* beginUndoRecording/endUndoRecording calls can be nested (inner calls are ignored, only one * beginUndoRecording/endUndoRecording calls can be nested (inner calls are ignored, only one
* undo operation is recorded.) * undo operation is recorded.)
* *
* @param label The label for the undo operation. Can be null but we should really try to put * @param label The label for the undo operation. Can be null but we should really try to put
* something meaningful if possible. * something meaningful if possible.
* @return True if the undo recording actually started, false if any kind of error occured. * @return True if the undo recording actually started, false if any kind of error occured.
@@ -652,7 +657,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
return false; return false;
} }
/** /**
* Ends an "undo recording" session. * Ends an "undo recording" session.
* <p/> * <p/>
@@ -671,14 +676,14 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
} }
} }
/** /**
* Creates an "undo recording" session by calling the undoableAction runnable * Creates an "undo recording" session by calling the undoableAction runnable
* using {@link #beginUndoRecording(String)} and {@link #endUndoRecording()}. * using {@link #beginUndoRecording(String)} and {@link #endUndoRecording()}.
* <p> * <p>
* You can nest several calls to {@link #wrapUndoRecording(String, Runnable)}, only one * You can nest several calls to {@link #wrapUndoRecording(String, Runnable)}, only one
* recording session will be created. * recording session will be created.
* *
* @param label The label for the undo operation. Can be null. Ideally we should really try * @param label The label for the undo operation. Can be null. Ideally we should really try
* to put something meaningful if possible. * to put something meaningful if possible.
*/ */
@@ -693,7 +698,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
} }
} }
/** /**
* Returns the XML {@link Document} or null if we can't get it * Returns the XML {@link Document} or null if we can't get it
*/ */
@@ -709,7 +714,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
} }
return null; return null;
} }
/** /**
* Returns the {@link IProject} for the edited file. * Returns the {@link IProject} for the edited file.
*/ */
@@ -719,16 +724,16 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
if (input instanceof FileEditorInput) { if (input instanceof FileEditorInput) {
FileEditorInput fileInput = (FileEditorInput)input; FileEditorInput fileInput = (FileEditorInput)input;
IFile inputFile = fileInput.getFile(); IFile inputFile = fileInput.getFile();
if (inputFile != null) { if (inputFile != null) {
return inputFile.getProject(); return inputFile.getProject();
} }
} }
} }
return null; return null;
} }
/** /**
* Returns the {@link AndroidTargetData} for the edited file. * Returns the {@link AndroidTargetData} for the edited file.
*/ */
@@ -738,22 +743,22 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
Sdk currentSdk = Sdk.getCurrent(); Sdk currentSdk = Sdk.getCurrent();
if (currentSdk != null) { if (currentSdk != null) {
IAndroidTarget target = currentSdk.getTarget(project); IAndroidTarget target = currentSdk.getTarget(project);
if (target != null) { if (target != null) {
return currentSdk.getTargetData(target); return currentSdk.getTargetData(target);
} }
} }
} }
return null; return null;
} }
/** /**
* Listen to changes in the underlying XML model in the structured editor. * Listen to changes in the underlying XML model in the structured editor.
*/ */
private class XmlModelStateListener implements IModelStateListener { private class XmlModelStateListener implements IModelStateListener {
/** /**
* A model is about to be changed. This typically is initiated by one * A model is about to be changed. This typically is initiated by one
* client of the model, to signal a large change and/or a change to the * client of the model, to signal a large change and/or a change to the
@@ -765,7 +770,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
public void modelAboutToBeChanged(IStructuredModel model) { public void modelAboutToBeChanged(IStructuredModel model) {
// pass // pass
} }
/** /**
* Signals that the changes foretold by modelAboutToBeChanged have been * Signals that the changes foretold by modelAboutToBeChanged have been
* made. A typical use might be to refresh, or to resume processing that * made. A typical use might be to refresh, or to resume processing that
@@ -776,7 +781,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
public void modelChanged(IStructuredModel model) { public void modelChanged(IStructuredModel model) {
xmlModelChanged(getXmlDocument(model)); xmlModelChanged(getXmlDocument(model));
} }
/** /**
* Notifies that a model's dirty state has changed, and passes that state * Notifies that a model's dirty state has changed, and passes that state
* in isDirty. A model becomes dirty when any change is made, and becomes * in isDirty. A model becomes dirty when any change is made, and becomes
@@ -787,7 +792,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
public void modelDirtyStateChanged(IStructuredModel model, boolean isDirty) { public void modelDirtyStateChanged(IStructuredModel model, boolean isDirty) {
// pass // pass
} }
/** /**
* A modelDeleted means the underlying resource has been deleted. The * A modelDeleted means the underlying resource has been deleted. The
* model itself is not removed from model management until all have * model itself is not removed from model management until all have
@@ -799,7 +804,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
public void modelResourceDeleted(IStructuredModel model) { public void modelResourceDeleted(IStructuredModel model) {
// pass // pass
} }
/** /**
* A model has been renamed or copied (as in saveAs..). In the renamed * A model has been renamed or copied (as in saveAs..). In the renamed
* case, the two paramenters are the same instance, and only contain the * case, the two paramenters are the same instance, and only contain the
@@ -810,14 +815,14 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
public void modelResourceMoved(IStructuredModel oldModel, IStructuredModel newModel) { public void modelResourceMoved(IStructuredModel oldModel, IStructuredModel newModel) {
// pass // pass
} }
/** /**
* This AndroidEditor implementation of IModelChangedListener is empty. * This AndroidEditor implementation of IModelChangedListener is empty.
*/ */
public void modelAboutToBeReinitialized(IStructuredModel structuredModel) { public void modelAboutToBeReinitialized(IStructuredModel structuredModel) {
// pass // pass
} }
/** /**
* This AndroidEditor implementation of IModelChangedListener is empty. * This AndroidEditor implementation of IModelChangedListener is empty.
*/ */

View File

@@ -32,8 +32,10 @@ import org.eclipse.swt.widgets.Composite;
*/ */
public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor { public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor {
/** The {@link ResourceType} that this reference attribute can accept. It can be null,
* in which case any reference type can be used. */
private ResourceType mResourceType; private ResourceType mResourceType;
/** /**
* Creates a reference attributes that can contain any type of resources. * Creates a reference attributes that can contain any type of resources.
* @param xmlLocalName The XML name of the attribute (case sensitive) * @param xmlLocalName The XML name of the attribute (case sensitive)
@@ -46,7 +48,7 @@ public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor
String tooltip) { String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip); super(xmlLocalName, uiName, nsUri, tooltip);
} }
/** /**
* Creates a reference attributes that can contain a reference to a specific * Creates a reference attributes that can contain a reference to a specific
* {@link ResourceType}. * {@link ResourceType}.
@@ -58,14 +60,20 @@ public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor
* See {@link SdkConstants#NS_RESOURCES} for a common value. * See {@link SdkConstants#NS_RESOURCES} for a common value.
* @param tooltip A non-empty tooltip string or null * @param tooltip A non-empty tooltip string or null
*/ */
public ReferenceAttributeDescriptor(ResourceType resourceType, public ReferenceAttributeDescriptor(ResourceType resourceType,
String xmlLocalName, String uiName, String nsUri, String xmlLocalName, String uiName, String nsUri,
String tooltip) { String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip); super(xmlLocalName, uiName, nsUri, tooltip);
mResourceType = resourceType; mResourceType = resourceType;
} }
/** Returns the {@link ResourceType} that this reference attribute can accept.
* It can be null, in which case any reference type can be used. */
public ResourceType getResourceType() {
return mResourceType;
}
/** /**
* @return A new {@link UiResourceAttributeNode} linked to this reference descriptor. * @return A new {@link UiResourceAttributeNode} linked to this reference descriptor.
*/ */
@@ -73,7 +81,7 @@ public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor
public UiAttributeNode createUiNode(UiElementNode uiParent) { public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiResourceAttributeNode(mResourceType, this, uiParent); return new UiResourceAttributeNode(mResourceType, this, uiParent);
} }
// ------- IPropertyDescriptor Methods // ------- IPropertyDescriptor Methods
@Override @Override

View File

@@ -35,9 +35,9 @@ import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.wizards.actions.AvdManagerAction; import com.android.ide.eclipse.adt.internal.wizards.actions.AvdManagerAction;
import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.NullSdkLog; import com.android.sdklib.NullSdkLog;
import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
@@ -274,16 +274,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* @param packageName the Android package name of the app * @param packageName the Android package name of the app
* @param debugPackageName the Android package name to debug * @param debugPackageName the Android package name to debug
* @param debuggable the debuggable value of the app, or null if not set. * @param debuggable the debuggable value of the app, or null if not set.
* @param requiredApiVersionNumber the api version required by the app, or * @param requiredApiVersionNumber the api version required by the app, or null if none.
* {@link AndroidManifestParser#INVALID_MIN_SDK} if none.
* @param launchAction the action to perform after app sync * @param launchAction the action to perform after app sync
* @param config the launch configuration * @param config the launch configuration
* @param launch the launch object * @param launch the launch object
*/ */
public void launch(final IProject project, String mode, IFile apk, public void launch(final IProject project, String mode, IFile apk,
String packageName, String debugPackageName, Boolean debuggable, int requiredApiVersionNumber, String packageName, String debugPackageName, Boolean debuggable,
final IAndroidLaunchAction launchAction, final AndroidLaunchConfiguration config, String requiredApiVersionNumber, final IAndroidLaunchAction launchAction,
final AndroidLaunch launch, IProgressMonitor monitor) { final AndroidLaunchConfiguration config, final AndroidLaunch launch,
IProgressMonitor monitor) {
String message = String.format("Performing %1$s", launchAction.getLaunchDescription()); String message = String.format("Performing %1$s", launchAction.getLaunchDescription());
AdtPlugin.printToConsole(project, message); AdtPlugin.printToConsole(project, message);
@@ -398,17 +398,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
} else { } else {
if (projectTarget.isPlatform()) { // means this can run on any device as long if (projectTarget.isPlatform()) { // means this can run on any device as long
// as api level is high enough // as api level is high enough
String apiString = d.getProperty(SdkManager.PROP_VERSION_SDK); AndroidVersion deviceVersion = Sdk.getDeviceVersion(d);
try { if (deviceVersion.canRun(projectTarget.getVersion())) {
int apiNumber = Integer.parseInt(apiString); // device is compatible with project
if (apiNumber >= projectTarget.getApiVersionNumber()) { compatibleRunningAvds.put(d, null);
// device is compatible with project continue;
compatibleRunningAvds.put(d, null);
continue;
}
} catch (NumberFormatException e) {
// do nothing, we'll consider it a non compatible device below.
} }
} else {
// for non project platform, we can't be sure if a device can
// run an application or not, since we don't query the device
// for the list of optional libraries that it supports.
} }
hasDevice = true; hasDevice = true;
} }
@@ -544,9 +543,12 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
AvdInfo defaultAvd = null; AvdInfo defaultAvd = null;
for (AvdInfo avd : avds) { for (AvdInfo avd : avds) {
if (projectTarget.isCompatibleBaseFor(avd.getTarget())) { if (projectTarget.isCompatibleBaseFor(avd.getTarget())) {
// at this point we can ignore the code name issue since
// IAndroidTarget.isCompatibleBaseFor() will already have filtered the non
// compatible AVDs.
if (defaultAvd == null || if (defaultAvd == null ||
avd.getTarget().getApiVersionNumber() < avd.getTarget().getVersion().getApiLevel() <
defaultAvd.getTarget().getApiVersionNumber()) { defaultAvd.getTarget().getVersion().getApiLevel()) {
defaultAvd = avd; defaultAvd = avd;
} }
} }
@@ -654,47 +656,67 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
if (device != null) { if (device != null) {
// check the app required API level versus the target device API level // check the app required API level versus the target device API level
String deviceApiVersionName = device.getProperty(IDevice.PROP_BUILD_VERSION); String deviceVersion = device.getProperty(IDevice.PROP_BUILD_VERSION);
String value = device.getProperty(IDevice.PROP_BUILD_VERSION_NUMBER); String deviceApiLevelString = device.getProperty(IDevice.PROP_BUILD_API_LEVEL);
int deviceApiVersionNumber = AndroidManifestParser.INVALID_MIN_SDK; String deviceCodeName = device.getProperty(IDevice.PROP_BUILD_CODENAME);
int deviceApiLevel = -1;
try { try {
deviceApiVersionNumber = Integer.parseInt(value); deviceApiLevel = Integer.parseInt(deviceApiLevelString);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// pass, we'll keep the deviceVersionNumber value at 0. // pass, we'll keep the apiLevel value at -1.
} }
if (launchInfo.getRequiredApiVersionNumber() == AndroidManifestParser.INVALID_MIN_SDK) { String requiredApiString = launchInfo.getRequiredApiVersionNumber();
// warn the API level requirement is not set. if (requiredApiString != null) {
int requiredApi = -1;
try {
requiredApi = Integer.parseInt(requiredApiString);
} catch (NumberFormatException e) {
// pass, we'll keep requiredApi value at -1.
}
if (requiredApi == -1) {
// this means the manifest uses a codename for minSdkVersion
// check that the device is using the same codename
if (requiredApiString.equals(deviceCodeName) == false) {
AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format(
"ERROR: Application requires a device running '%1$s'!",
requiredApiString));
return false;
}
} else {
// app requires a specific API level
if (deviceApiLevel == -1) {
AdtPlugin.printToConsole(launchInfo.getProject(),
"WARNING: Unknown device API version!");
} else if (deviceApiLevel < requiredApi) {
String msg = String.format(
"ERROR: Application requires API version %1$d. Device API version is %2$d (Android %3$s).",
requiredApi, deviceApiLevel, deviceVersion);
AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
// abort the launch
return false;
}
}
} else {
// warn the application API level requirement is not set.
AdtPlugin.printErrorToConsole(launchInfo.getProject(), AdtPlugin.printErrorToConsole(launchInfo.getProject(),
"WARNING: Application does not specify an API level requirement!"); "WARNING: Application does not specify an API level requirement!");
// and display the target device API level (if known) // and display the target device API level (if known)
if (deviceApiVersionName == null || if (deviceApiLevel == -1) {
deviceApiVersionNumber == AndroidManifestParser.INVALID_MIN_SDK) {
AdtPlugin.printErrorToConsole(launchInfo.getProject(), AdtPlugin.printErrorToConsole(launchInfo.getProject(),
"WARNING: Unknown device API version!"); "WARNING: Unknown device API version!");
} else { } else {
AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format( AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format(
"Device API version is %1$d (Android %2$s)", deviceApiVersionNumber, "Device API version is %1$d (Android %2$s)", deviceApiLevel,
deviceApiVersionName)); deviceVersion));
}
} else { // app requires a specific API level
if (deviceApiVersionName == null ||
deviceApiVersionNumber == AndroidManifestParser.INVALID_MIN_SDK) {
AdtPlugin.printToConsole(launchInfo.getProject(),
"WARNING: Unknown device API version!");
} else if (deviceApiVersionNumber < launchInfo.getRequiredApiVersionNumber()) {
String msg = String.format(
"ERROR: Application requires API version %1$d. Device API version is %2$d (Android %3$s).",
launchInfo.getRequiredApiVersionNumber(), deviceApiVersionNumber,
deviceApiVersionName);
AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
// abort the launch
return false;
} }
} }
// now checks that the device/app can be debugged (if needed) // now checks that the device/app can be debugged (if needed)
if (device.isEmulator() == false && launchInfo.isDebugMode()) { if (device.isEmulator() == false && launchInfo.isDebugMode()) {
String debuggableDevice = device.getProperty(IDevice.PROP_DEBUGGABLE); String debuggableDevice = device.getProperty(IDevice.PROP_DEBUGGABLE);
@@ -1521,7 +1543,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
/** /**
* Get the stderr/stdout outputs of a process and return when the process is done. * Get the stderr/stdout outputs of a process and return when the process is done.
* Both <b>must</b> be read or the process will block on windows. * Both <b>must</b> be read or the process will block on windows.
* @param process The process to get the ouput from * @param process The process to get the output from
*/ */
private void grabEmulatorOutput(final Process process) { private void grabEmulatorOutput(final Process process) {
// read the lines as they come. if null is returned, it's // read the lines as they come. if null is returned, it's

View File

@@ -17,7 +17,6 @@
package com.android.ide.eclipse.adt.internal.launch; package com.android.ide.eclipse.adt.internal.launch;
import com.android.ddmlib.IDevice; import com.android.ddmlib.IDevice;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestParser;
import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
@@ -28,14 +27,14 @@ import org.eclipse.core.runtime.IProgressMonitor;
* application is launched. * application is launched.
*/ */
public final class DelayedLaunchInfo { public final class DelayedLaunchInfo {
/** /**
* Used to indicate behavior when Android app already exists * Used to indicate behavior when Android app already exists
*/ */
enum InstallRetryMode { enum InstallRetryMode {
NEVER, ALWAYS, PROMPT; NEVER, ALWAYS, PROMPT;
} }
/** The device on which to launch the app */ /** The device on which to launch the app */
private IDevice mDevice = null; private IDevice mDevice = null;
@@ -44,22 +43,21 @@ public final class DelayedLaunchInfo {
/** Package name */ /** Package name */
private final String mPackageName; private final String mPackageName;
/** Debug package name */ /** Debug package name */
private final String mDebugPackageName; private final String mDebugPackageName;
/** IFile to the package (.apk) file */ /** IFile to the package (.apk) file */
private final IFile mPackageFile; private final IFile mPackageFile;
/** debuggable attribute of the manifest file. */ /** debuggable attribute of the manifest file. */
private final Boolean mDebuggable; private final Boolean mDebuggable;
/** Required ApiVersionNumber by the app. {@link AndroidManifestParser#INVALID_MIN_SDK} means /** Required Api level by the app. null means no requirements */
* no requirements */ private final String mRequiredApiVersionNumber;
private final int mRequiredApiVersionNumber;
private InstallRetryMode mRetryMode = InstallRetryMode.NEVER; private InstallRetryMode mRetryMode = InstallRetryMode.NEVER;
/** Launch action. */ /** Launch action. */
private final IAndroidLaunchAction mLaunchAction; private final IAndroidLaunchAction mLaunchAction;
@@ -78,23 +76,22 @@ public final class DelayedLaunchInfo {
/** cancellation state of launch */ /** cancellation state of launch */
private boolean mCancelled = false; private boolean mCancelled = false;
/** /**
* Basic constructor with activity and package info. * Basic constructor with activity and package info.
* *
* @param project the eclipse project that corresponds to Android app * @param project the eclipse project that corresponds to Android app
* @param packageName package name of Android app * @param packageName package name of Android app
* @param debugPackageName the package name of the Andriod app to debug * @param debugPackageName the package name of the Andriod app to debug
* @param launchAction action to perform after app install * @param launchAction action to perform after app install
* @param pack IFile to the package (.apk) file * @param pack IFile to the package (.apk) file
* @param debuggable debuggable attribute of the app's manifest file. * @param debuggable debuggable attribute of the app's manifest file.
* @param requiredApiVersionNumber required SDK version by the app. * @param requiredApiVersionNumber required SDK version by the app. null means no requirements.
* {@link AndroidManifestParser#INVALID_MIN_SDK} means no requirements.
* @param launch the launch object * @param launch the launch object
* @param monitor progress monitor for launch * @param monitor progress monitor for launch
*/ */
public DelayedLaunchInfo(IProject project, String packageName, String debugPackageName, public DelayedLaunchInfo(IProject project, String packageName, String debugPackageName,
IAndroidLaunchAction launchAction, IFile pack, Boolean debuggable, IAndroidLaunchAction launchAction, IFile pack, Boolean debuggable,
int requiredApiVersionNumber, AndroidLaunch launch, IProgressMonitor monitor) { String requiredApiVersionNumber, AndroidLaunch launch, IProgressMonitor monitor) {
mProject = project; mProject = project;
mPackageName = packageName; mPackageName = packageName;
mDebugPackageName = debugPackageName; mDebugPackageName = debugPackageName;
@@ -112,7 +109,7 @@ public final class DelayedLaunchInfo {
public IDevice getDevice() { public IDevice getDevice() {
return mDevice; return mDevice;
} }
/** /**
* Set the device on which to launch the app * Set the device on which to launch the app
*/ */
@@ -153,16 +150,16 @@ public final class DelayedLaunchInfo {
} }
/** /**
* @return true if Android app is marked as debuggable in its manifest * @return true if Android app is marked as debuggable in its manifest
*/ */
public Boolean getDebuggable() { public Boolean getDebuggable() {
return mDebuggable; return mDebuggable;
} }
/** /**
* @return the required api version number for the Android app * @return the required api version number for the Android app.
*/ */
public int getRequiredApiVersionNumber() { public String getRequiredApiVersionNumber() {
return mRequiredApiVersionNumber; return mRequiredApiVersionNumber;
} }
@@ -195,7 +192,7 @@ public final class DelayedLaunchInfo {
} }
/** /**
* @return the launch progress monitor * @return the launch progress monitor
*/ */
public IProgressMonitor getMonitor() { public IProgressMonitor getMonitor() {
return mMonitor; return mMonitor;
@@ -230,7 +227,7 @@ public final class DelayedLaunchInfo {
} }
/** /**
* Set if launch has been cancelled * Set if launch has been cancelled
*/ */
public void setCancelled(boolean cancelled) { public void setCancelled(boolean cancelled) {
this.mCancelled = cancelled; this.mCancelled = cancelled;

View File

@@ -27,6 +27,7 @@ import com.android.ddmuilib.TableHelper;
import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.ddms.DdmsPlugin; import com.android.ide.eclipse.ddms.DdmsPlugin;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.internal.widgets.AvdSelector; import com.android.sdkuilib.internal.widgets.AvdSelector;
@@ -131,26 +132,19 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
case 2: case 2:
// check for compatibility. // check for compatibility.
if (device.isEmulator() == false) { // physical device if (device.isEmulator() == false) { // physical device
// get the api level of the device // get the version of the device
try { AndroidVersion deviceVersion = Sdk.getDeviceVersion(device);
String apiValue = device.getProperty( if (deviceVersion == null) {
IDevice.PROP_BUILD_VERSION_NUMBER); return mWarningImage;
if (apiValue != null) { } else {
int api = Integer.parseInt(apiValue); if (deviceVersion.canRun(mProjectTarget.getVersion()) == false) {
if (api >= mProjectTarget.getApiVersionNumber()) { return mNoMatchImage;
// if the project is compiling against an add-on, the optional
// API may be missing from the device.
return mProjectTarget.isPlatform() ?
mMatchImage : mWarningImage;
} else {
return mNoMatchImage;
}
} else {
return mWarningImage;
} }
} catch (NumberFormatException e) {
// lets consider the device non compatible // if the project is compiling against an add-on,
return mNoMatchImage; // the optional API may be missing from the device.
return mProjectTarget.isPlatform() ?
mMatchImage : mWarningImage;
} }
} else { } else {
// get the AvdInfo // get the AvdInfo
@@ -411,6 +405,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
offsetComp.setLayout(layout); offsetComp.setLayout(layout);
mPreferredAvdSelector = new AvdSelector(offsetComp, mPreferredAvdSelector = new AvdSelector(offsetComp,
mSdk.getSdkLocation(),
mSdk.getAvdManager(), mSdk.getAvdManager(),
new NonRunningAvdFilter(), new NonRunningAvdFilter(),
DisplayMode.SIMPLE_SELECTION); DisplayMode.SIMPLE_SELECTION);

View File

@@ -195,7 +195,9 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
// create the selector with no manager, we'll reset the manager every time this is // create the selector with no manager, we'll reset the manager every time this is
// displayed to ensure we have the latest one (dialog is reused but SDK could have // displayed to ensure we have the latest one (dialog is reused but SDK could have
// been changed in between. // been changed in between.
mPreferredAvdSelector = new AvdSelector(offsetComp, null /* avd manager */, mPreferredAvdSelector = new AvdSelector(offsetComp,
Sdk.getCurrent().getSdkLocation(),
null /* avd manager */,
DisplayMode.SIMPLE_CHECK); DisplayMode.SIMPLE_CHECK);
mPreferredAvdSelector.setTableHeightHint(100); mPreferredAvdSelector.setTableHeightHint(100);
mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() { mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {

View File

@@ -51,7 +51,7 @@ public class AndroidManifestParser {
private final static String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$ private final static String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
private final static String ATTRIBUTE_PROCESS = "process"; //$NON-NLS-$ private final static String ATTRIBUTE_PROCESS = "process"; //$NON-NLS-$
private final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; //$NON-NLS-$ private final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; //$NON-NLS-$
private final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$ public final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$
private final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; //$NON-NLS-1$ private final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; //$NON-NLS-1$
private final static String ATTRIBUTE_EXPORTED = "exported"; //$NON-NLS-1$ private final static String ATTRIBUTE_EXPORTED = "exported"; //$NON-NLS-1$
private final static String NODE_MANIFEST = "manifest"; //$NON-NLS-1$ private final static String NODE_MANIFEST = "manifest"; //$NON-NLS-1$
@@ -76,8 +76,6 @@ public class AndroidManifestParser {
private final static String ACTION_MAIN = "android.intent.action.MAIN"; //$NON-NLS-1$ private final static String ACTION_MAIN = "android.intent.action.MAIN"; //$NON-NLS-1$
private final static String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; //$NON-NLS-1$ private final static String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; //$NON-NLS-1$
public final static int INVALID_MIN_SDK = -1;
/** /**
* Instrumentation info obtained from manifest * Instrumentation info obtained from manifest
*/ */
@@ -179,9 +177,8 @@ public class AndroidManifestParser {
private Set<String> mProcesses = null; private Set<String> mProcesses = null;
/** debuggable attribute value. If null, the attribute is not present. */ /** debuggable attribute value. If null, the attribute is not present. */
private Boolean mDebuggable = null; private Boolean mDebuggable = null;
/** API level requirement. if {@link AndroidManifestParser#INVALID_MIN_SDK} /** API level requirement. if null the attribute was not present. */
* the attribute was not present. */ private String mApiLevelRequirement = null;
private int mApiLevelRequirement = INVALID_MIN_SDK;
/** List of all instrumentations declared by the manifest */ /** List of all instrumentations declared by the manifest */
private final ArrayList<Instrumentation> mInstrumentations = private final ArrayList<Instrumentation> mInstrumentations =
new ArrayList<Instrumentation>(); new ArrayList<Instrumentation>();
@@ -258,10 +255,9 @@ public class AndroidManifestParser {
} }
/** /**
* Returns the <code>minSdkVersion</code> attribute, or * Returns the <code>minSdkVersion</code> attribute, or null if it's not set.
* {@link AndroidManifestParser#INVALID_MIN_SDK} if it's not set.
*/ */
int getApiLevelRequirement() { String getApiLevelRequirement() {
return mApiLevelRequirement; return mApiLevelRequirement;
} }
@@ -331,16 +327,8 @@ public class AndroidManifestParser {
mValidLevel++; mValidLevel++;
} else if (NODE_USES_SDK.equals(localName)) { } else if (NODE_USES_SDK.equals(localName)) {
value = getAttributeValue(attributes, ATTRIBUTE_MIN_SDK_VERSION, mApiLevelRequirement = getAttributeValue(attributes,
true /* hasNamespace */); ATTRIBUTE_MIN_SDK_VERSION, true /* hasNamespace */);
if (value != null) {
try {
mApiLevelRequirement = Integer.parseInt(value);
} catch (NumberFormatException e) {
handleError(e, -1 /* lineNumber */);
}
}
} else if (NODE_INSTRUMENTATION.equals(localName)) { } else if (NODE_INSTRUMENTATION.equals(localName)) {
processInstrumentationNode(attributes); processInstrumentationNode(attributes);
} }
@@ -636,7 +624,7 @@ public class AndroidManifestParser {
private final Activity mLauncherActivity; private final Activity mLauncherActivity;
private final String[] mProcesses; private final String[] mProcesses;
private final Boolean mDebuggable; private final Boolean mDebuggable;
private final int mApiLevelRequirement; private final String mApiLevelRequirement;
private final Instrumentation[] mInstrumentations; private final Instrumentation[] mInstrumentations;
private final String[] mLibraries; private final String[] mLibraries;
@@ -904,10 +892,9 @@ public class AndroidManifestParser {
} }
/** /**
* Returns the <code>minSdkVersion</code> attribute, or {@link #INVALID_MIN_SDK} * Returns the <code>minSdkVersion</code> attribute, or null if it's not set.
* if it's not set.
*/ */
public int getApiLevelRequirement() { public String getApiLevelRequirement() {
return mApiLevelRequirement; return mApiLevelRequirement;
} }
@@ -939,13 +926,13 @@ public class AndroidManifestParser {
* @param launcherActivity the launcher activity parser from the manifest. * @param launcherActivity the launcher activity parser from the manifest.
* @param processes the list of custom processes declared in the manifest. * @param processes the list of custom processes declared in the manifest.
* @param debuggable the debuggable attribute, or null if not set. * @param debuggable the debuggable attribute, or null if not set.
* @param apiLevelRequirement the minSdkVersion attribute value or 0 if not set. * @param apiLevelRequirement the minSdkVersion attribute value or null if not set.
* @param instrumentations the list of instrumentations parsed from the manifest. * @param instrumentations the list of instrumentations parsed from the manifest.
* @param libraries the list of libraries in use parsed from the manifest. * @param libraries the list of libraries in use parsed from the manifest.
*/ */
private AndroidManifestParser(String javaPackage, Activity[] activities, private AndroidManifestParser(String javaPackage, Activity[] activities,
Activity launcherActivity, String[] processes, Boolean debuggable, Activity launcherActivity, String[] processes, Boolean debuggable,
int apiLevelRequirement, Instrumentation[] instrumentations, String[] libraries) { String apiLevelRequirement, Instrumentation[] instrumentations, String[] libraries) {
mJavaPackage = javaPackage; mJavaPackage = javaPackage;
mActivities = activities; mActivities = activities;
mLauncherActivity = launcherActivity; mLauncherActivity = launcherActivity;

View File

@@ -4,7 +4,7 @@
* Licensed under the Eclipse Public License, Version 1.0 (the "License"); * 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 not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.eclipse.org/org/documents/epl-v10.php * http://www.eclipse.org/org/documents/epl-v10.php
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
@@ -47,7 +47,7 @@ import org.eclipse.ui.part.FileEditorInput;
* Action executed when the "Extract String" menu item is invoked. * Action executed when the "Extract String" menu item is invoked.
* <p/> * <p/>
* The intent of the action is to start a refactoring that extracts a source string and * The intent of the action is to start a refactoring that extracts a source string and
* replaces it by an Android string resource ID. * replaces it by an Android string resource ID.
* <p/> * <p/>
* Workflow: * Workflow:
* <ul> * <ul>
@@ -74,6 +74,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
/** Keep track of the current workbench window. */ /** Keep track of the current workbench window. */
private IWorkbenchWindow mWindow; private IWorkbenchWindow mWindow;
private ITextSelection mSelection; private ITextSelection mSelection;
private IEditorPart mEditor;
private IFile mFile; private IFile mFile;
/** /**
@@ -103,11 +104,12 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
mSelection = null; mSelection = null;
mFile = null; mFile = null;
if (selection instanceof ITextSelection) { if (selection instanceof ITextSelection) {
mSelection = (ITextSelection) selection; mSelection = (ITextSelection) selection;
if (mSelection.getLength() > 0) { if (mSelection.getLength() > 0) {
mFile = getSelectedFile(); mEditor = getActiveEditor();
mFile = getSelectedFile(mEditor);
} }
} }
@@ -119,7 +121,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
*/ */
public void run(IAction action) { public void run(IAction action) {
if (mSelection != null && mFile != null) { if (mSelection != null && mFile != null) {
ExtractStringRefactoring ref = new ExtractStringRefactoring(mFile, mSelection); ExtractStringRefactoring ref = new ExtractStringRefactoring(mFile, mEditor, mSelection);
RefactoringWizard wizard = new ExtractStringWizard(ref, mFile.getProject()); RefactoringWizard wizard = new ExtractStringWizard(ref, mFile.getProject());
RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
try { try {
@@ -130,6 +132,21 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
} }
} }
/**
* Returns the active editor (hopefully matching our selection) or null.
*/
private IEditorPart getActiveEditor() {
IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (wwin != null) {
IWorkbenchPage page = wwin.getActivePage();
if (page != null) {
return page.getActiveEditor();
}
}
return null;
}
/** /**
* Returns the active {@link IFile} (hopefully matching our selection) or null. * Returns the active {@link IFile} (hopefully matching our selection) or null.
* The file is only returned if it's a file from a project with an Android nature. * The file is only returned if it's a file from a project with an Android nature.
@@ -138,33 +155,26 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
* for the refactoring. This check is performed when the refactoring is invoked since * for the refactoring. This check is performed when the refactoring is invoked since
* it can then produce meaningful error messages as needed. * it can then produce meaningful error messages as needed.
*/ */
private IFile getSelectedFile() { private IFile getSelectedFile(IEditorPart editor) {
IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (editor != null) {
if (wwin != null) { IEditorInput input = editor.getEditorInput();
IWorkbenchPage page = wwin.getActivePage();
if (page != null) { if (input instanceof FileEditorInput) {
IEditorPart editor = page.getActiveEditor(); FileEditorInput fi = (FileEditorInput) input;
if (editor != null) { IFile file = fi.getFile();
IEditorInput input = editor.getEditorInput(); if (file.exists()) {
IProject proj = file.getProject();
if (input instanceof FileEditorInput) { try {
FileEditorInput fi = (FileEditorInput) input; if (proj != null && proj.hasNature(AndroidConstants.NATURE)) {
IFile file = fi.getFile(); return file;
if (file.exists()) {
IProject proj = file.getProject();
try {
if (proj != null && proj.hasNature(AndroidConstants.NATURE)) {
return file;
}
} catch (CoreException e) {
// ignore
}
} }
} catch (CoreException e) {
// ignore
} }
} }
} }
} }
return null; return null;
} }
} }

View File

@@ -33,6 +33,8 @@ import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener; 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.GridData;
import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Combo;
@@ -42,6 +44,7 @@ import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Text;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -59,7 +62,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
private final IProject mProject; private final IProject mProject;
/** Text field where the user enters the new ID to be generated or replaced with. */ /** Text field where the user enters the new ID to be generated or replaced with. */
private Text mStringIdField; private Combo mStringIdCombo;
/** Text field where the user enters the new string value. */ /** Text field where the user enters the new string value. */
private Text mStringValueField; private Text mStringValueField;
/** The configuration selector, to select the resource path of the XML file. */ /** The configuration selector, to select the resource path of the XML file. */
@@ -132,7 +135,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
// line: Textfield for string value (based on selection, if any) // line: Textfield for string value (based on selection, if any)
Label label = new Label(group, SWT.NONE); Label label = new Label(group, SWT.NONE);
label.setText("String"); label.setText("&String");
String selectedString = ref.getTokenString(); String selectedString = ref.getTokenString();
@@ -156,24 +159,31 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
label = new Label(group, SWT.NONE); label = new Label(group, SWT.NONE);
if (ref.getMode() == ExtractStringRefactoring.Mode.EDIT_SOURCE) { if (ref.getMode() == ExtractStringRefactoring.Mode.EDIT_SOURCE) {
label.setText("Replace by R.string."); label.setText("&Replace by R.string.");
} else if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) { } else if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) {
label.setText("New R.string."); label.setText("New &R.string.");
} else { } else {
label.setText("ID R.string."); label.setText("ID &R.string.");
} }
mStringIdField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER); mStringIdCombo = new Combo(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER | SWT.DROP_DOWN);
mStringIdField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mStringIdCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mStringIdField.setText(guessId(selectedString)); mStringIdCombo.setText(guessId(selectedString));
mStringIdCombo.forceFocus();
ref.setNewStringId(mStringIdField.getText().trim()); ref.setNewStringId(mStringIdCombo.getText().trim());
mStringIdField.addModifyListener(new ModifyListener() { mStringIdCombo.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) { public void modifyText(ModifyEvent e) {
validatePage(); validatePage();
} }
}); });
mStringIdCombo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
validatePage();
}
});
} }
/** /**
@@ -196,7 +206,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
Label label; Label label;
label = new Label(group, SWT.NONE); label = new Label(group, SWT.NONE);
label.setText("Configuration:"); label.setText("&Configuration:");
mConfigSelector = new ConfigurationSelector(group); mConfigSelector = new ConfigurationSelector(group);
GridData gd = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL); GridData gd = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
@@ -210,7 +220,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
// line: selection of the output file // line: selection of the output file
label = new Label(group, SWT.NONE); label = new Label(group, SWT.NONE);
label.setText("Resource file:"); label.setText("Resource &file:");
mResFileCombo = new Combo(group, SWT.DROP_DOWN); mResFileCombo = new Combo(group, SWT.DROP_DOWN);
mResFileCombo.select(0); mResFileCombo.select(0);
@@ -269,7 +279,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
// Analyze fatal errors. // Analyze fatal errors.
String text = mStringIdField.getText().trim(); String text = mStringIdCombo.getText().trim();
if (text == null || text.length() < 1) { if (text == null || text.length() < 1) {
setErrorMessage("Please provide a resource ID."); setErrorMessage("Please provide a resource ID.");
success = false; success = false;
@@ -313,9 +323,12 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
ref.setTargetFile(resFile); ref.setTargetFile(resFile);
sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile); sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile);
if (mXmlHelper.isResIdDuplicate(mProject, resFile, text)) { String idValue = mXmlHelper.valueOfStringId(mProject, resFile, text);
String msg = String.format("There's already a string item called '%1$s' in %2$s.", if (idValue != null) {
text, resFile); String msg = String.format("%1$s already contains a string ID '%2$s' with value '%3$s'.",
resFile,
text,
idValue);
if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) { if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) {
setErrorMessage(msg); setErrorMessage(msg);
success = false; success = false;
@@ -341,6 +354,23 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
return success; return success;
} }
private void updateStringValueCombo() {
String resFile = mResFileCombo.getText();
Map<String, String> ids = mXmlHelper.getResIdsForFile(mProject, resFile);
// get the current text from the combo, to make sure we don't change it
String currText = mStringIdCombo.getText();
// erase the choices and fill with the given ids
mStringIdCombo.removeAll();
mStringIdCombo.setItems(ids.keySet().toArray(new String[ids.size()]));
// set the current text to preserve it in case it changed
if (!currText.equals(mStringIdCombo.getText())) {
mStringIdCombo.setText(currText);
}
}
public class OnConfigSelectorUpdated implements Runnable, ModifyListener { public class OnConfigSelectorUpdated implements Runnable, ModifyListener {
/** Regex pattern to parse a valid res path: it reads (/res/folder-name/)+(filename). */ /** Regex pattern to parse a valid res path: it reads (/res/folder-name/)+(filename). */
@@ -457,6 +487,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
} }
// finally validate the whole page // finally validate the whole page
updateStringValueCombo();
validatePage(); validatePage();
} }
@@ -509,6 +540,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
} }
} }
updateStringValueCombo();
validatePage(); validatePage();
} }
} }

View File

@@ -4,7 +4,7 @@
* Licensed under the Eclipse Public License, Version 1.0 (the "License"); * 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 not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.eclipse.org/org/documents/epl-v10.php * http://www.eclipse.org/org/documents/epl-v10.php
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
@@ -22,72 +22,88 @@ import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.Map;
import java.util.TreeMap;
import javax.xml.xpath.XPath; import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathExpressionException;
/** /**
* * An helper utility to get IDs out of an Android XML resource file.
*/ */
class XmlStringFileHelper { class XmlStringFileHelper {
/** A temporary cache of R.string IDs defined by a given xml file. The key is the /** A temporary cache of R.string IDs defined by a given xml file. The key is the
* project path of the file, the data is a set of known string Ids for that file. */ * project path of the file, the data is a set of known string Ids for that file.
private HashMap<String,HashSet<String>> mResIdCache; *
* Map type: map [String filename] => map [String id => String value].
*/
private HashMap<String, Map<String, String>> mResIdCache =
new HashMap<String, Map<String, String>>();
/** An instance of XPath, created lazily on demand. */ /** An instance of XPath, created lazily on demand. */
private XPath mXPath; private XPath mXPath;
public XmlStringFileHelper() { public XmlStringFileHelper() {
} }
/** /**
* Utility method used by the wizard to check whether the given string ID is already * Utility method used by the wizard to retrieve the actual value definition of a given
* defined in the XML file which path is given. * string ID.
* *
* @param project The project contain the XML file. * @param project The project contain the XML file.
* @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml". * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml".
* The given file may or may not exist. * The given file may or may not exist.
* @param stringId The string ID to find. * @param stringId The string ID to find.
* @return True if such a string ID is already defined. * @return The value string if the ID is defined, null otherwise.
*/ */
public boolean isResIdDuplicate(IProject project, String xmlFileWsPath, String stringId) { public String valueOfStringId(IProject project, String xmlFileWsPath, String stringId) {
// This is going to be called many times on the same file. Map<String, String> cache = getResIdsForFile(project, xmlFileWsPath);
// Build a cache of the existing IDs for a given file. return cache.get(stringId);
if (mResIdCache == null) { }
mResIdCache = new HashMap<String, HashSet<String>>();
} /**
HashSet<String> cache = mResIdCache.get(xmlFileWsPath); * Utility method that retrieves all the *string* IDs defined in the given Android resource
* file. The instance maintains an internal cache so a given file is retrieved only once.
* Callers should consider the set to be read-only.
*
* @param project The project contain the XML file.
* @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml".
* The given file may or may not exist.
* @return The map of string IDs => values defined in the given file. Cached. Never null.
*/
public Map<String, String> getResIdsForFile(IProject project, String xmlFileWsPath) {
Map<String, String> cache = mResIdCache.get(xmlFileWsPath);
if (cache == null) { if (cache == null) {
cache = getResIdsForFile(project, xmlFileWsPath); cache = internalGetResIdsForFile(project, xmlFileWsPath);
mResIdCache.put(xmlFileWsPath, cache); mResIdCache.put(xmlFileWsPath, cache);
} }
return cache;
return cache.contains(stringId);
} }
/** /**
* Extract all the defined string IDs from a given file using XPath. * Extract all the defined string IDs from a given file using XPath.
* @param project The project contain the XML file. * @param project The project contain the XML file.
* @param xmlFileWsPath The project path of the file to parse. It may not exist. * @param xmlFileWsPath The project path of the file to parse. It may not exist.
* @return The set of all string IDs defined in the file. The returned set is always non * @return The map of all string IDs => values defined in the file.
* null. It is empty if the file does not exist. * The returned set is always non null. It is empty if the file does not exist.
*/ */
private HashSet<String> getResIdsForFile(IProject project, String xmlFileWsPath) { private Map<String, String> internalGetResIdsForFile(IProject project, String xmlFileWsPath) {
HashSet<String> ids = new HashSet<String>(); TreeMap<String, String> ids = new TreeMap<String, String>();
if (mXPath == null) { if (mXPath == null) {
mXPath = AndroidXPathFactory.newXPath(); mXPath = AndroidXPathFactory.newXPath();
} }
// Access the project that contains the resource that contains the compilation unit // Access the project that contains the resource that contains the compilation unit
IResource resource = project.getFile(xmlFileWsPath); IResource resource = project.getFile(xmlFileWsPath);
if (resource != null && resource.exists() && resource.getType() == IResource.FILE) { if (resource != null && resource.exists() && resource.getType() == IResource.FILE) {
InputSource source; InputSource source;
try { try {
@@ -97,25 +113,31 @@ class XmlStringFileHelper {
// <resources> // <resources>
// <string name="ID">something</string> // <string name="ID">something</string>
// </resources> // </resources>
String xpathExpr = "/resources/string/@name"; //$NON-NLS-1$ String xpathExpr = "/resources/string"; //$NON-NLS-1$
Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET); Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET);
if (result instanceof NodeList) { if (result instanceof NodeList) {
NodeList list = (NodeList) result; NodeList list = (NodeList) result;
for (int n = list.getLength() - 1; n >= 0; n--) { for (int n = list.getLength() - 1; n >= 0; n--) {
String id = list.item(n).getNodeValue(); Node strNode = list.item(n);
ids.add(id); NamedNodeMap attrs = strNode.getAttributes();
Node nameAttr = attrs.getNamedItem("name"); //$NON-NLS-1$
if (nameAttr != null) {
String id = nameAttr.getNodeValue();
String text = strNode.getTextContent();
ids.put(id, text);
}
} }
} }
} catch (CoreException e1) { } catch (CoreException e1) {
// IFile.getContents failed. Ignore. // IFile.getContents failed. Ignore.
} catch (XPathExpressionException e) { } catch (XPathExpressionException e2) {
// mXPath.evaluate failed. Ignore. // mXPath.evaluate failed. Ignore.
} }
} }
return ids; return ids;
} }

View File

@@ -366,7 +366,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
} }
} }
return result.toString(); return result == null ? null : result.toString();
} }
public int compareTo(FolderConfiguration folderConfig) { public int compareTo(FolderConfiguration folderConfig) {

View File

@@ -66,10 +66,10 @@ import javax.management.InvalidAttributeValueException;
* <li>Resource ID from <code>android.R</code></li> * <li>Resource ID from <code>android.R</code></li>
* <li>The list of permissions values from <code>android.Manifest$permission</code></li> * <li>The list of permissions values from <code>android.Manifest$permission</code></li>
* <li></li> * <li></li>
* </ul> * </ul>
*/ */
public final class AndroidTargetParser { public final class AndroidTargetParser {
private static final String TAG = "Framework Resource Parser"; private static final String TAG = "Framework Resource Parser";
private final IAndroidTarget mAndroidTarget; private final IAndroidTarget mAndroidTarget;
@@ -79,11 +79,11 @@ public final class AndroidTargetParser {
public AndroidTargetParser(IAndroidTarget platformTarget) { public AndroidTargetParser(IAndroidTarget platformTarget) {
mAndroidTarget = platformTarget; mAndroidTarget = platformTarget;
} }
/** /**
* Parses the framework, collects all interesting information and stores them in the * Parses the framework, collects all interesting information and stores them in the
* {@link IAndroidTarget} given to the constructor. * {@link IAndroidTarget} given to the constructor.
* *
* @param monitor A progress monitor. Can be null. Caller is responsible for calling done. * @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. * @return True if the SDK path was valid and parsing has been attempted.
*/ */
@@ -92,7 +92,7 @@ public final class AndroidTargetParser {
SubMonitor progress = SubMonitor.convert(monitor, SubMonitor progress = SubMonitor.convert(monitor,
String.format("Parsing SDK %1$s", mAndroidTarget.getName()), String.format("Parsing SDK %1$s", mAndroidTarget.getName()),
14); 14);
AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget); AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget);
// load DX. // load DX.
@@ -103,22 +103,22 @@ public final class AndroidTargetParser {
String.format("dx.jar loading failed for target '%1$s'", String.format("dx.jar loading failed for target '%1$s'",
mAndroidTarget.getFullName())); mAndroidTarget.getFullName()));
} }
// we have loaded dx. // we have loaded dx.
targetData.setDexWrapper(dexWrapper); targetData.setDexWrapper(dexWrapper);
progress.worked(1); progress.worked(1);
// parse the rest of the data. // parse the rest of the data.
AndroidJarLoader classLoader = AndroidJarLoader classLoader =
new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR));
preload(classLoader, progress.newChild(40, SubMonitor.SUPPRESS_NONE)); preload(classLoader, progress.newChild(40, SubMonitor.SUPPRESS_NONE));
if (progress.isCanceled()) { if (progress.isCanceled()) {
return Status.CANCEL_STATUS; return Status.CANCEL_STATUS;
} }
// get the resource Ids. // get the resource Ids.
progress.subTask("Resource IDs"); progress.subTask("Resource IDs");
IResourceRepository frameworkRepository = collectResourceIds(classLoader); IResourceRepository frameworkRepository = collectResourceIds(classLoader);
@@ -172,7 +172,7 @@ public final class AndroidTargetParser {
progress.subTask("Widgets and layouts"); progress.subTask("Widgets and layouts");
collectLayoutClasses(classLoader, attrsXmlParser, mainList, groupList, collectLayoutClasses(classLoader, attrsXmlParser, mainList, groupList,
progress.newChild(1)); progress.newChild(1));
if (progress.isCanceled()) { if (progress.isCanceled()) {
return Status.CANCEL_STATUS; return Status.CANCEL_STATUS;
} }
@@ -180,7 +180,7 @@ public final class AndroidTargetParser {
ViewClassInfo[] layoutViewsInfo = mainList.toArray(new ViewClassInfo[mainList.size()]); ViewClassInfo[] layoutViewsInfo = mainList.toArray(new ViewClassInfo[mainList.size()]);
ViewClassInfo[] layoutGroupsInfo = groupList.toArray( ViewClassInfo[] layoutGroupsInfo = groupList.toArray(
new ViewClassInfo[groupList.size()]); new ViewClassInfo[groupList.size()]);
// collect the preferences classes. // collect the preferences classes.
mainList.clear(); mainList.clear();
groupList.clear(); groupList.clear();
@@ -203,17 +203,17 @@ public final class AndroidTargetParser {
Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues(); Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues();
Map<String, DeclareStyleableInfo> xmlAppWidgetMap = null; Map<String, DeclareStyleableInfo> xmlAppWidgetMap = null;
if (mAndroidTarget.getApiVersionNumber() >= 3) { if (mAndroidTarget.getVersion().getApiLevel() >= 3) {
xmlAppWidgetMap = collectAppWidgetDefinitions(attrsXmlParser); xmlAppWidgetMap = collectAppWidgetDefinitions(attrsXmlParser);
} }
if (progress.isCanceled()) { if (progress.isCanceled()) {
return Status.CANCEL_STATUS; return Status.CANCEL_STATUS;
} }
// From the information that was collected, create the pieces that will be put in // From the information that was collected, create the pieces that will be put in
// the PlatformData object. // the PlatformData object.
AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors(); AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors();
manifestDescriptors.updateDescriptors(manifestMap); manifestDescriptors.updateDescriptors(manifestMap);
progress.worked(1); progress.worked(1);
@@ -244,16 +244,16 @@ public final class AndroidTargetParser {
preferencesInfo, preferencesInfo,
preferenceGroupsInfo); preferenceGroupsInfo);
progress.worked(1); progress.worked(1);
// load the framework resources. // load the framework resources.
ProjectResources resources = ResourceManager.getInstance().loadFrameworkResources( ProjectResources resources = ResourceManager.getInstance().loadFrameworkResources(
mAndroidTarget); mAndroidTarget);
progress.worked(1); progress.worked(1);
// now load the layout lib bridge // now load the layout lib bridge
LayoutBridge layoutBridge = loadLayoutBridge(); LayoutBridge layoutBridge = loadLayoutBridge();
progress.worked(1); progress.worked(1);
// and finally create the PlatformData with all that we loaded. // and finally create the PlatformData with all that we loaded.
targetData.setExtraData(frameworkRepository, targetData.setExtraData(frameworkRepository,
manifestDescriptors, manifestDescriptors,
@@ -270,7 +270,7 @@ public final class AndroidTargetParser {
mAndroidTarget.getOptionalLibraries(), mAndroidTarget.getOptionalLibraries(),
resources, resources,
layoutBridge); layoutBridge);
Sdk.getCurrent().setTargetData(mAndroidTarget, targetData); Sdk.getCurrent().setTargetData(mAndroidTarget, targetData);
return Status.OK_STATUS; return Status.OK_STATUS;
@@ -285,7 +285,7 @@ public final class AndroidTargetParser {
* Preloads all "interesting" classes from the framework SDK jar. * Preloads all "interesting" classes from the framework SDK jar.
* <p/> * <p/>
* Currently this preloads all classes from the framework jar * Currently this preloads all classes from the framework jar
* *
* @param classLoader The framework SDK jar classloader * @param classLoader The framework SDK jar classloader
* @param monitor A progress monitor. Can be null. Caller is responsible for calling done. * @param monitor A progress monitor. Can be null. Caller is responsible for calling done.
*/ */
@@ -303,7 +303,7 @@ public final class AndroidTargetParser {
/** /**
* Creates an IResourceRepository for the framework resources. * Creates an IResourceRepository for the framework resources.
* *
* @param classLoader The framework SDK jar classloader * @param classLoader The framework SDK jar classloader
* @return a map of the resources, or null if it failed. * @return a map of the resources, or null if it failed.
*/ */
@@ -311,7 +311,7 @@ public final class AndroidTargetParser {
AndroidJarLoader classLoader) { AndroidJarLoader classLoader) {
try { try {
Class<?> r = classLoader.loadClass(AndroidConstants.CLASS_R); Class<?> r = classLoader.loadClass(AndroidConstants.CLASS_R);
if (r != null) { if (r != null) {
Map<ResourceType, List<ResourceItem>> map = parseRClass(r); Map<ResourceType, List<ResourceItem>> map = parseRClass(r);
if (map != null) { if (map != null) {
@@ -321,23 +321,23 @@ public final class AndroidTargetParser {
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
AdtPlugin.logAndPrintError(e, TAG, AdtPlugin.logAndPrintError(e, TAG,
"Collect resource IDs failed, class %1$s not found in %2$s", //$NON-NLS-1$ "Collect resource IDs failed, class %1$s not found in %2$s", //$NON-NLS-1$
AndroidConstants.CLASS_R, AndroidConstants.CLASS_R,
mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR));
} }
return null; return null;
} }
/** /**
* Parse the R class and build the resource map. * Parse the R class and build the resource map.
* *
* @param rClass the Class object representing the Resources. * @param rClass the Class object representing the Resources.
* @return a map of the resource or null * @return a map of the resource or null
*/ */
private Map<ResourceType, List<ResourceItem>> parseRClass(Class<?> rClass) { private Map<ResourceType, List<ResourceItem>> parseRClass(Class<?> rClass) {
// get the sub classes. // get the sub classes.
Class<?>[] classes = rClass.getClasses(); Class<?>[] classes = rClass.getClasses();
if (classes.length > 0) { if (classes.length > 0) {
HashMap<ResourceType, List<ResourceItem>> map = HashMap<ResourceType, List<ResourceItem>> map =
new HashMap<ResourceType, List<ResourceItem>>(); new HashMap<ResourceType, List<ResourceItem>>();
@@ -346,30 +346,30 @@ public final class AndroidTargetParser {
for (int c = 0 ; c < classes.length ; c++) { for (int c = 0 ; c < classes.length ; c++) {
Class<?> subClass = classes[c]; Class<?> subClass = classes[c];
String name = subClass.getSimpleName(); String name = subClass.getSimpleName();
// get the matching ResourceType // get the matching ResourceType
ResourceType type = ResourceType.getEnum(name); ResourceType type = ResourceType.getEnum(name);
if (type != null) { if (type != null) {
List<ResourceItem> list = new ArrayList<ResourceItem>(); List<ResourceItem> list = new ArrayList<ResourceItem>();
map.put(type, list); map.put(type, list);
Field[] fields = subClass.getFields(); Field[] fields = subClass.getFields();
for (Field f : fields) { for (Field f : fields) {
list.add(new ResourceItem(f.getName())); list.add(new ResourceItem(f.getName()));
} }
} }
} }
return map; return map;
} }
return null; return null;
} }
/** /**
* Loads, collects and returns the list of default permissions from the framework. * Loads, collects and returns the list of default permissions from the framework.
* *
* @param classLoader The framework SDK jar classloader * @param classLoader The framework SDK jar classloader
* @return a non null (but possibly empty) array containing the permission values. * @return a non null (but possibly empty) array containing the permission values.
*/ */
@@ -377,12 +377,12 @@ public final class AndroidTargetParser {
try { try {
Class<?> permissionClass = Class<?> permissionClass =
classLoader.loadClass(AndroidConstants.CLASS_MANIFEST_PERMISSION); classLoader.loadClass(AndroidConstants.CLASS_MANIFEST_PERMISSION);
if (permissionClass != null) { if (permissionClass != null) {
ArrayList<String> list = new ArrayList<String>(); ArrayList<String> list = new ArrayList<String>();
Field[] fields = permissionClass.getFields(); Field[] fields = permissionClass.getFields();
for (Field f : fields) { for (Field f : fields) {
int modifiers = f.getModifiers(); int modifiers = f.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) && if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) &&
@@ -403,23 +403,23 @@ public final class AndroidTargetParser {
} }
} }
} }
return list.toArray(new String[list.size()]); return list.toArray(new String[list.size()]);
} }
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
AdtPlugin.logAndPrintError(e, TAG, AdtPlugin.logAndPrintError(e, TAG,
"Collect permissions failed, class %1$s not found in %2$s", //$NON-NLS-1$ "Collect permissions failed, class %1$s not found in %2$s", //$NON-NLS-1$
AndroidConstants.CLASS_MANIFEST_PERMISSION, AndroidConstants.CLASS_MANIFEST_PERMISSION,
mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR));
} }
return new String[0]; return new String[0];
} }
/** /**
* Loads and collects the action and category default values from the framework. * 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. * The values are added to the <code>actions</code> and <code>categories</code> lists.
* *
* @param activityActions the list which will receive the activity action values. * @param activityActions the list which will receive the activity action values.
* @param broadcastActions the list which will receive the broadcast 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 serviceActions the list which will receive the service action values.
@@ -481,7 +481,7 @@ public final class AndroidTargetParser {
/** /**
* Collects all layout classes information from the class loader and the * Collects all layout classes information from the class loader and the
* attrs.xml and sets the corresponding structures in the resource manager. * attrs.xml and sets the corresponding structures in the resource manager.
* *
* @param classLoader The framework SDK jar classloader in case we cannot get the widget from * @param classLoader The framework SDK jar classloader in case we cannot get the widget from
* the platform directly * the platform directly
* @param attrsXmlParser The parser of the attrs.xml file * @param attrsXmlParser The parser of the attrs.xml file
@@ -491,7 +491,7 @@ public final class AndroidTargetParser {
*/ */
private void collectLayoutClasses(AndroidJarLoader classLoader, private void collectLayoutClasses(AndroidJarLoader classLoader,
AttrsXmlParser attrsXmlParser, AttrsXmlParser attrsXmlParser,
Collection<ViewClassInfo> mainList, Collection<ViewClassInfo> groupList, Collection<ViewClassInfo> mainList, Collection<ViewClassInfo> groupList,
IProgressMonitor monitor) { IProgressMonitor monitor) {
LayoutParamsParser ldp = null; LayoutParamsParser ldp = null;
try { try {
@@ -510,7 +510,7 @@ public final class AndroidTargetParser {
ldp = new LayoutParamsParser(classLoader, attrsXmlParser); ldp = new LayoutParamsParser(classLoader, attrsXmlParser);
} }
ldp.parseLayoutClasses(monitor); ldp.parseLayoutClasses(monitor);
List<ViewClassInfo> views = ldp.getViews(); List<ViewClassInfo> views = ldp.getViews();
List<ViewClassInfo> groups = ldp.getGroups(); List<ViewClassInfo> groups = ldp.getGroups();
@@ -523,7 +523,7 @@ public final class AndroidTargetParser {
/** /**
* Collects all preferences definition information from the attrs.xml and * Collects all preferences definition information from the attrs.xml and
* sets the corresponding structures in the resource manager. * sets the corresponding structures in the resource manager.
* *
* @param classLoader The framework SDK jar classloader * @param classLoader The framework SDK jar classloader
* @param attrsXmlParser The parser of the attrs.xml file * @param attrsXmlParser The parser of the attrs.xml file
* @param mainList the Collection to receive the main list of {@link ViewClassInfo}. * @param mainList the Collection to receive the main list of {@link ViewClassInfo}.
@@ -534,13 +534,13 @@ public final class AndroidTargetParser {
AttrsXmlParser attrsXmlParser, Collection<ViewClassInfo> mainList, AttrsXmlParser attrsXmlParser, Collection<ViewClassInfo> mainList,
Collection<ViewClassInfo> groupList, IProgressMonitor monitor) { Collection<ViewClassInfo> groupList, IProgressMonitor monitor) {
LayoutParamsParser ldp = new LayoutParamsParser(classLoader, attrsXmlParser); LayoutParamsParser ldp = new LayoutParamsParser(classLoader, attrsXmlParser);
try { try {
ldp.parsePreferencesClasses(monitor); ldp.parsePreferencesClasses(monitor);
List<ViewClassInfo> prefs = ldp.getViews(); List<ViewClassInfo> prefs = ldp.getViews();
List<ViewClassInfo> groups = ldp.getGroups(); List<ViewClassInfo> groups = ldp.getGroups();
if (prefs != null && groups != null) { if (prefs != null && groups != null) {
mainList.addAll(prefs); mainList.addAll(prefs);
groupList.addAll(groups); groupList.addAll(groups);
@@ -548,7 +548,7 @@ public final class AndroidTargetParser {
} catch (NoClassDefFoundError e) { } catch (NoClassDefFoundError e) {
AdtPlugin.logAndPrintError(e, TAG, AdtPlugin.logAndPrintError(e, TAG,
"Collect preferences failed, class %1$s not found in %2$s", "Collect preferences failed, class %1$s not found in %2$s",
e.getMessage(), e.getMessage(),
classLoader.getSource()); classLoader.getSource());
} catch (Throwable e) { } catch (Throwable e) {
AdtPlugin.log(e, "Android Framework Parser: failed to collect preference classes"); //$NON-NLS-1$ AdtPlugin.log(e, "Android Framework Parser: failed to collect preference classes"); //$NON-NLS-1$
@@ -559,7 +559,7 @@ public final class AndroidTargetParser {
/** /**
* Collects all menu definition information from the attrs.xml and returns it. * Collects all menu definition information from the attrs.xml and returns it.
* *
* @param attrsXmlParser The parser of the attrs.xml file * @param attrsXmlParser The parser of the attrs.xml file
*/ */
private Map<String, DeclareStyleableInfo> collectMenuDefinitions( private Map<String, DeclareStyleableInfo> collectMenuDefinitions(
@@ -575,18 +575,18 @@ public final class AndroidTargetParser {
AdtPlugin.log(IStatus.WARNING, AdtPlugin.log(IStatus.WARNING,
"Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ "Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$
key, attrsXmlParser.getOsAttrsXmlPath()); key, attrsXmlParser.getOsAttrsXmlPath());
AdtPlugin.printErrorToConsole("Android Framework Parser", AdtPlugin.printErrorToConsole("Android Framework Parser",
String.format("Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ String.format("Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$
key, attrsXmlParser.getOsAttrsXmlPath())); key, attrsXmlParser.getOsAttrsXmlPath()));
} }
} }
return Collections.unmodifiableMap(map2); return Collections.unmodifiableMap(map2);
} }
/** /**
* Collects all searchable definition information from the attrs.xml and returns it. * Collects all searchable definition information from the attrs.xml and returns it.
* *
* @param attrsXmlParser The parser of the attrs.xml file * @param attrsXmlParser The parser of the attrs.xml file
*/ */
private Map<String, DeclareStyleableInfo> collectSearchableDefinitions( private Map<String, DeclareStyleableInfo> collectSearchableDefinitions(
@@ -612,7 +612,7 @@ public final class AndroidTargetParser {
/** /**
* Collects all appWidgetProviderInfo definition information from the attrs.xml and returns it. * Collects all appWidgetProviderInfo definition information from the attrs.xml and returns it.
* *
* @param attrsXmlParser The parser of the attrs.xml file * @param attrsXmlParser The parser of the attrs.xml file
*/ */
private Map<String, DeclareStyleableInfo> collectAppWidgetDefinitions( private Map<String, DeclareStyleableInfo> collectAppWidgetDefinitions(
@@ -657,13 +657,13 @@ public final class AndroidTargetParser {
AdtPlugin.log(IStatus.ERROR, "layoutlib.jar is missing!"); //$NON-NLS-1$ AdtPlugin.log(IStatus.ERROR, "layoutlib.jar is missing!"); //$NON-NLS-1$
} else { } else {
URL url = f.toURL(); URL url = f.toURL();
// create a class loader. Because this jar reference interfaces // create a class loader. Because this jar reference interfaces
// that are in the editors plugin, it's important to provide // that are in the editors plugin, it's important to provide
// a parent class loader. // a parent class loader.
layoutBridge.classLoader = new URLClassLoader(new URL[] { url }, layoutBridge.classLoader = new URLClassLoader(new URL[] { url },
this.getClass().getClassLoader()); this.getClass().getClassLoader());
// load the class // load the class
Class<?> clazz = layoutBridge.classLoader.loadClass(AndroidConstants.CLASS_BRIDGE); Class<?> clazz = layoutBridge.classLoader.loadClass(AndroidConstants.CLASS_BRIDGE);
if (clazz != null) { if (clazz != null) {
@@ -676,7 +676,7 @@ public final class AndroidTargetParser {
} }
} }
} }
if (layoutBridge.bridge == null) { if (layoutBridge.bridge == null) {
layoutBridge.status = LoadStatus.FAILED; layoutBridge.status = LoadStatus.FAILED;
AdtPlugin.log(IStatus.ERROR, "Failed to load " + AndroidConstants.CLASS_BRIDGE); //$NON-NLS-1$ AdtPlugin.log(IStatus.ERROR, "Failed to load " + AndroidConstants.CLASS_BRIDGE); //$NON-NLS-1$
@@ -688,7 +688,7 @@ public final class AndroidTargetParser {
// the first version of the api did not have this method // the first version of the api did not have this method
layoutBridge.apiLevel = 1; layoutBridge.apiLevel = 1;
} }
// and mark the lib as loaded. // and mark the lib as loaded.
layoutBridge.status = LoadStatus.LOADED; layoutBridge.status = LoadStatus.LOADED;
} }
@@ -698,7 +698,7 @@ public final class AndroidTargetParser {
// log the error. // log the error.
AdtPlugin.log(t, "Failed to load the LayoutLib"); AdtPlugin.log(t, "Failed to load the LayoutLib");
} }
return layoutBridge; return layoutBridge;
} }
} }

View File

@@ -16,12 +16,14 @@
package com.android.ide.eclipse.adt.internal.sdk; package com.android.ide.eclipse.adt.internal.sdk;
import com.android.ddmlib.IDevice;
import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer; import com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor.IProjectListener; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor.IProjectListener;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog; import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkConstants;
@@ -394,6 +396,21 @@ public class Sdk implements IProjectListener {
return mAvdManager; return mAvdManager;
} }
public static AndroidVersion getDeviceVersion(IDevice device) {
try {
Map<String, String> props = device.getProperties();
String apiLevel = props.get(IDevice.PROP_BUILD_API_LEVEL);
if (apiLevel == null) {
return null;
}
return new AndroidVersion(Integer.parseInt(apiLevel),
props.get((IDevice.PROP_BUILD_CODENAME)));
} catch (NumberFormatException e) {
return null;
}
}
private Sdk(SdkManager manager, AvdManager avdManager) { private Sdk(SdkManager manager, AvdManager avdManager) {
mManager = manager; mManager = manager;
mAvdManager = avdManager; mAvdManager = avdManager;

View File

@@ -840,25 +840,21 @@ public class NewProjectCreationPage extends WizardPage {
return; return;
} }
try { String minSdkVersion = mInfo.getMinSdkVersion();
int version = Integer.parseInt(mInfo.getMinSdkVersion());
// Before changing, compare with the currently selected one, if any. // Before changing, compare with the currently selected one, if any.
// There can be multiple targets with the same sdk api version, so don't change // There can be multiple targets with the same sdk api version, so don't change
// it if it's already at the right version. // it if it's already at the right version.
IAndroidTarget curr_target = mInfo.getSdkTarget(); IAndroidTarget curr_target = mInfo.getSdkTarget();
if (curr_target != null && curr_target.getApiVersionNumber() == version) { if (curr_target != null && curr_target.getVersion().equals(minSdkVersion)) {
return; return;
} }
for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
if (target.getApiVersionNumber() == version) { if (target.getVersion().equals(minSdkVersion)) {
mSdkTargetSelector.setSelection(target); mSdkTargetSelector.setSelection(target);
break; break;
}
} }
} catch (NumberFormatException e) {
// ignore
} }
} }
@@ -873,7 +869,7 @@ public class NewProjectCreationPage extends WizardPage {
if (target != null) { if (target != null) {
mInternalMinSdkVersionUpdate = true; mInternalMinSdkVersionUpdate = true;
mMinSdkVersionField.setText(Integer.toString(target.getApiVersionNumber())); mMinSdkVersionField.setText(target.getVersion().getApiString());
mInternalMinSdkVersionUpdate = false; mInternalMinSdkVersionUpdate = false;
} }
} }
@@ -932,7 +928,7 @@ public class NewProjectCreationPage extends WizardPage {
String packageName = null; String packageName = null;
Activity activity = null; Activity activity = null;
String activityName = null; String activityName = null;
int minSdkVersion = AndroidManifestParser.INVALID_MIN_SDK; String minSdkVersion = null;
try { try {
packageName = manifestData.getPackage(); packageName = manifestData.getPackage();
minSdkVersion = manifestData.getApiLevelRequirement(); minSdkVersion = manifestData.getApiLevelRequirement();
@@ -1033,17 +1029,13 @@ public class NewProjectCreationPage extends WizardPage {
} }
} }
if (!foundTarget && minSdkVersion != AndroidManifestParser.INVALID_MIN_SDK) { if (!foundTarget && minSdkVersion != null) {
try { for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { if (target.getVersion().equals(minSdkVersion)) {
if (target.getApiVersionNumber() == minSdkVersion) { mSdkTargetSelector.setSelection(target);
mSdkTargetSelector.setSelection(target); foundTarget = true;
foundTarget = true; break;
break;
}
} }
} catch(NumberFormatException e) {
// ignore
} }
} }
@@ -1059,9 +1051,9 @@ public class NewProjectCreationPage extends WizardPage {
if (!foundTarget) { if (!foundTarget) {
mInternalMinSdkVersionUpdate = true; mInternalMinSdkVersionUpdate = true;
mMinSdkVersionField.setText( if (minSdkVersion != null) {
minSdkVersion == AndroidManifestParser.INVALID_MIN_SDK ? "" //$NON-NLS-1$ mMinSdkVersionField.setText(minSdkVersion);
: Integer.toString(minSdkVersion)); }
mInternalMinSdkVersionUpdate = false; mInternalMinSdkVersionUpdate = false;
} }
} }
@@ -1269,21 +1261,10 @@ public class NewProjectCreationPage extends WizardPage {
return MSG_NONE; return MSG_NONE;
} }
int version = AndroidManifestParser.INVALID_MIN_SDK; if (mInfo.getSdkTarget() != null &&
try { mInfo.getSdkTarget().getVersion().equals(mInfo.getMinSdkVersion()) == false) {
// If not empty, it must be a valid integer > 0
version = Integer.parseInt(mInfo.getMinSdkVersion());
} catch (NumberFormatException e) {
// ignore
}
if (version < 1) {
return setStatus("Min SDK Version must be an integer > 0.", MSG_ERROR);
}
if (mInfo.getSdkTarget() != null && mInfo.getSdkTarget().getApiVersionNumber() != version) {
return setStatus("The API level for the selected SDK target does not match the Min SDK version.", return setStatus("The API level for the selected SDK target does not match the Min SDK version.",
MSG_WARNING); mInfo.getSdkTarget().getVersion().isPreview() ? MSG_ERROR : MSG_WARNING);
} }
return MSG_NONE; return MSG_NONE;

View File

@@ -821,7 +821,7 @@ public class NewTestProjectCreationPage extends WizardPage {
if (manifestData != null) { if (manifestData != null) {
String appName = String.format("%1$sTest", project.getName()); String appName = String.format("%1$sTest", project.getName());
String packageName = manifestData.getPackage(); String packageName = manifestData.getPackage();
int minSdkVersion = manifestData.getApiLevelRequirement(); String minSdkVersion = manifestData.getApiLevelRequirement();
IAndroidTarget sdkTarget = null; IAndroidTarget sdkTarget = null;
if (Sdk.getCurrent() != null) { if (Sdk.getCurrent() != null) {
sdkTarget = Sdk.getCurrent().getTarget(project); sdkTarget = Sdk.getCurrent().getTarget(project);
@@ -859,9 +859,9 @@ public class NewTestProjectCreationPage extends WizardPage {
if (!mMinSdkVersionModifiedByUser) { if (!mMinSdkVersionModifiedByUser) {
mInternalMinSdkVersionUpdate = true; mInternalMinSdkVersionUpdate = true;
mMinSdkVersionField.setText( if (minSdkVersion != null) {
minSdkVersion != AndroidManifestParser.INVALID_MIN_SDK ? mMinSdkVersionField.setText(minSdkVersion);
Integer.toString(minSdkVersion) : ""); //$NON-NLS-1$ }
if (sdkTarget == null) { if (sdkTarget == null) {
updateSdkSelectorToMatchMinSdkVersion(); updateSdkSelectorToMatchMinSdkVersion();
} }
@@ -1052,22 +1052,18 @@ public class NewTestProjectCreationPage extends WizardPage {
* that matches. * that matches.
*/ */
private void updateSdkSelectorToMatchMinSdkVersion() { private void updateSdkSelectorToMatchMinSdkVersion() {
try { String minSdkVersion = mInfo.getMinSdkVersion();
int version = Integer.parseInt(mInfo.getMinSdkVersion());
IAndroidTarget curr_target = mInfo.getSdkTarget(); IAndroidTarget curr_target = mInfo.getSdkTarget();
if (curr_target != null && curr_target.getApiVersionNumber() == version) { if (curr_target != null && curr_target.getVersion().equals(minSdkVersion)) {
return;
}
for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
if (target.getVersion().equals(minSdkVersion)) {
mSdkTargetSelector.setSelection(target);
return; return;
} }
for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
if (target.getApiVersionNumber() == version) {
mSdkTargetSelector.setSelection(target);
return;
}
}
} catch (NumberFormatException e) {
// ignore
} }
} }
@@ -1086,7 +1082,7 @@ public class NewTestProjectCreationPage extends WizardPage {
if (target != null) { if (target != null) {
mInternalMinSdkVersionUpdate = true; mInternalMinSdkVersionUpdate = true;
mMinSdkVersionField.setText(Integer.toString(target.getApiVersionNumber())); mMinSdkVersionField.setText(target.getVersion().getApiString());
mInternalMinSdkVersionUpdate = false; mInternalMinSdkVersionUpdate = false;
} }
@@ -1261,21 +1257,10 @@ public class NewTestProjectCreationPage extends WizardPage {
return MSG_NONE; return MSG_NONE;
} }
int version = AndroidManifestParser.INVALID_MIN_SDK; if (mInfo.getSdkTarget() != null &&
try { mInfo.getSdkTarget().getVersion().equals(mInfo.getMinSdkVersion()) == false) {
// If not empty, it must be a valid integer > 0
version = Integer.parseInt(mInfo.getMinSdkVersion());
} catch (NumberFormatException e) {
// ignore
}
if (version < 1) {
return setStatus("Min SDK Version must be an integer > 0.", MSG_ERROR);
}
if (mInfo.getSdkTarget() != null && mInfo.getSdkTarget().getApiVersionNumber() != version) {
return setStatus("The API level for the selected SDK target does not match the Min SDK version.", return setStatus("The API level for the selected SDK target does not match the Min SDK version.",
MSG_WARNING); mInfo.getSdkTarget().getVersion().isPreview() ? MSG_ERROR : MSG_WARNING);
} }
return MSG_NONE; return MSG_NONE;

View File

@@ -87,10 +87,10 @@ class NewXmlFileCreationPage extends WizardPage {
private final String mDefaultAttrs; private final String mDefaultAttrs;
private final String mDefaultRoot; private final String mDefaultRoot;
private final int mTargetApiLevel; private final int mTargetApiLevel;
public TypeInfo(String uiName, public TypeInfo(String uiName,
String tooltip, String tooltip,
ResourceFolderType resFolderType, ResourceFolderType resFolderType,
Object rootSeed, Object rootSeed,
String defaultRoot, String defaultRoot,
String xmlns, String xmlns,
@@ -110,12 +110,12 @@ class NewXmlFileCreationPage extends WizardPage {
String getUiName() { String getUiName() {
return mUiName; return mUiName;
} }
/** Returns the tooltip for the resource type. Can be null. */ /** Returns the tooltip for the resource type. Can be null. */
String getTooltip() { String getTooltip() {
return mTooltip; return mTooltip;
} }
/** /**
* Returns the name of the {@link ResourceFolderType}. * Returns the name of the {@link ResourceFolderType}.
* Never null but not necessarily unique, * Never null but not necessarily unique,
@@ -124,7 +124,7 @@ class NewXmlFileCreationPage extends WizardPage {
String getResFolderName() { String getResFolderName() {
return mResFolderType.getName(); return mResFolderType.getName();
} }
/** /**
* Returns the matching {@link ResourceFolderType}. * Returns the matching {@link ResourceFolderType}.
* Never null but not necessarily unique, * Never null but not necessarily unique,
@@ -138,16 +138,16 @@ class NewXmlFileCreationPage extends WizardPage {
void setWidget(Button widget) { void setWidget(Button widget) {
mWidget = widget; mWidget = widget;
} }
/** Returns the radio button associate with the resource type. Can be null. */ /** Returns the radio button associate with the resource type. Can be null. */
Button getWidget() { Button getWidget() {
return mWidget; return mWidget;
} }
/** /**
* Returns the seed used to fill the root element values. * Returns the seed used to fill the root element values.
* The seed might be either a String, a String array, an {@link ElementDescriptor}, * The seed might be either a String, a String array, an {@link ElementDescriptor},
* a {@link DocumentDescriptor} or null. * a {@link DocumentDescriptor} or null.
*/ */
Object getRootSeed() { Object getRootSeed() {
return mRootSeed; return mRootSeed;
@@ -278,7 +278,7 @@ class NewXmlFileCreationPage extends WizardPage {
private static final String RES_FOLDER_ABS = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP; private static final String RES_FOLDER_ABS = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
/** Relative destination folder root, e.g. "res/" */ /** Relative destination folder root, e.g. "res/" */
private static final String RES_FOLDER_REL = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP; private static final String RES_FOLDER_REL = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
private IProject mProject; private IProject mProject;
private Text mProjectTextField; private Text mProjectTextField;
private Button mProjectBrowseButton; private Button mProjectBrowseButton;
@@ -297,7 +297,7 @@ class NewXmlFileCreationPage extends WizardPage {
private TypeInfo mCurrentTypeInfo; private TypeInfo mCurrentTypeInfo;
// --- UI creation --- // --- UI creation ---
/** /**
* Constructs a new {@link NewXmlFileCreationPage}. * Constructs a new {@link NewXmlFileCreationPage}.
* <p/> * <p/>
@@ -314,9 +314,9 @@ class NewXmlFileCreationPage extends WizardPage {
/** /**
* Called by the parent Wizard to create the UI for this Wizard Page. * Called by the parent Wizard to create the UI for this Wizard Page.
* *
* {@inheritDoc} * {@inheritDoc}
* *
* @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
*/ */
public void createControl(Composite parent) { public void createControl(Composite parent) {
@@ -347,7 +347,7 @@ class NewXmlFileCreationPage extends WizardPage {
installTargetChangeListener(); installTargetChangeListener();
validatePage(); validatePage();
} }
private void installTargetChangeListener() { private void installTargetChangeListener() {
mSdkTargetChangeListener = new ITargetChangeListener() { mSdkTargetChangeListener = new ITargetChangeListener() {
public void onProjectTargetChange(IProject changedProject) { public void onProjectTargetChange(IProject changedProject) {
@@ -364,18 +364,18 @@ class NewXmlFileCreationPage extends WizardPage {
} }
} }
}; };
AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener); AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener);
} }
@Override @Override
public void dispose() { public void dispose() {
if (mSdkTargetChangeListener != null) { if (mSdkTargetChangeListener != null) {
AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener); AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener);
mSdkTargetChangeListener = null; mSdkTargetChangeListener = null;
} }
super.dispose(); super.dispose();
} }
@@ -399,7 +399,7 @@ class NewXmlFileCreationPage extends WizardPage {
public String getWsFolderPath() { public String getWsFolderPath() {
return mWsFolderPathTextField == null ? "" : mWsFolderPathTextField.getText(); //$NON-NLS-1$ return mWsFolderPathTextField == null ? "" : mWsFolderPathTextField.getText(); //$NON-NLS-1$
} }
/** /**
* Returns an {@link IFile} on the destination file. * Returns an {@link IFile} on the destination file.
@@ -426,7 +426,7 @@ class NewXmlFileCreationPage extends WizardPage {
/** /**
* Returns the {@link TypeInfo} for the currently selected type radio button. * Returns the {@link TypeInfo} for the currently selected type radio button.
* Returns null if no radio button is selected. * Returns null if no radio button is selected.
* *
* @return A {@link TypeInfo} or null. * @return A {@link TypeInfo} or null.
*/ */
public TypeInfo getSelectedType() { public TypeInfo getSelectedType() {
@@ -439,10 +439,10 @@ class NewXmlFileCreationPage extends WizardPage {
} }
return type; return type;
} }
/** /**
* Returns the selected root element string, if any. * Returns the selected root element string, if any.
* *
* @return The selected root element string or null. * @return The selected root element string or null.
*/ */
public String getRootElement() { public String getRootElement() {
@@ -457,7 +457,7 @@ class NewXmlFileCreationPage extends WizardPage {
/** /**
* Helper method to create a new GridData with an horizontal span. * Helper method to create a new GridData with an horizontal span.
* *
* @param horizSpan The number of cells for the horizontal span. * @param horizSpan The number of cells for the horizontal span.
* @return A new GridData with the horizontal span. * @return A new GridData with the horizontal span.
*/ */
@@ -469,7 +469,7 @@ class NewXmlFileCreationPage extends WizardPage {
/** /**
* Helper method to create a new GridData with an horizontal span and a style. * Helper method to create a new GridData with an horizontal span and a style.
* *
* @param horizSpan The number of cells for the horizontal span. * @param horizSpan The number of cells for the horizontal span.
* @param style The style, e.g. {@link GridData#FILL_HORIZONTAL} * @param style The style, e.g. {@link GridData#FILL_HORIZONTAL}
* @return A new GridData with the horizontal span and the style. * @return A new GridData with the horizontal span and the style.
@@ -482,7 +482,7 @@ class NewXmlFileCreationPage extends WizardPage {
/** /**
* Helper method that creates an empty cell in the parent composite. * Helper method that creates an empty cell in the parent composite.
* *
* @param parent The parent composite. * @param parent The parent composite.
*/ */
private void emptyCell(Composite parent) { private void emptyCell(Composite parent) {
@@ -491,7 +491,7 @@ class NewXmlFileCreationPage extends WizardPage {
/** /**
* Pads the parent with empty cells to match the number of columns of the parent grid. * Pads the parent with empty cells to match the number of columns of the parent grid.
* *
* @param parent A grid layout with NUM_COL columns * @param parent A grid layout with NUM_COL columns
* @param col The current number of columns used. * @param col The current number of columns used.
* @return 0, the new number of columns used, for convenience. * @return 0, the new number of columns used, for convenience.
@@ -511,7 +511,7 @@ class NewXmlFileCreationPage extends WizardPage {
*/ */
private void createProjectGroup(Composite parent) { private void createProjectGroup(Composite parent) {
int col = 0; int col = 0;
// project name // project name
String tooltip = "The Android Project where the new resource file will be created."; String tooltip = "The Android Project where the new resource file will be created.";
Label label = new Label(parent, SWT.NONE); Label label = new Label(parent, SWT.NONE);
@@ -542,7 +542,7 @@ class NewXmlFileCreationPage extends WizardPage {
++col; ++col;
col = padWithEmptyCells(parent, col); col = padWithEmptyCells(parent, col);
// file name // file name
tooltip = "The name of the resource file to create."; tooltip = "The name of the resource file to create.";
label = new Label(parent, SWT.NONE); label = new Label(parent, SWT.NONE);
@@ -572,7 +572,7 @@ class NewXmlFileCreationPage extends WizardPage {
// separator // separator
Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
label.setLayoutData(newGridData(NUM_COL, GridData.GRAB_HORIZONTAL)); label.setLayoutData(newGridData(NUM_COL, GridData.GRAB_HORIZONTAL));
// label before type radios // label before type radios
label = new Label(parent, SWT.NONE); label = new Label(parent, SWT.NONE);
label.setText("What type of resource would you like to create?"); label.setText("What type of resource would you like to create?");
@@ -584,7 +584,7 @@ class NewXmlFileCreationPage extends WizardPage {
padWithEmptyCells(parent, 2); padWithEmptyCells(parent, 2);
grid.setLayout(new GridLayout(NUM_COL, true /*makeColumnsEqualWidth*/)); grid.setLayout(new GridLayout(NUM_COL, true /*makeColumnsEqualWidth*/));
SelectionListener radioListener = new SelectionAdapter() { SelectionListener radioListener = new SelectionAdapter() {
@Override @Override
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
@@ -594,7 +594,7 @@ class NewXmlFileCreationPage extends WizardPage {
} }
} }
}; };
int n = sTypes.length; int n = sTypes.length;
int num_lines = (n + NUM_COL/2) / NUM_COL; int num_lines = (n + NUM_COL/2) / NUM_COL;
for (int line = 0, k = 0; line < num_lines; line++) { for (int line = 0, k = 0; line < num_lines; line++) {
@@ -627,7 +627,7 @@ class NewXmlFileCreationPage extends WizardPage {
mConfigSelector.setLayoutData(gd); mConfigSelector.setLayoutData(gd);
mConfigSelector.setOnChangeListener(new onConfigSelectorUpdated()); mConfigSelector.setOnChangeListener(new onConfigSelectorUpdated());
emptyCell(parent); emptyCell(parent);
// folder name // folder name
String tooltip = "The folder where the file will be generated, relative to the project."; String tooltip = "The folder where the file will be generated, relative to the project.";
label = new Label(parent, SWT.NONE); label = new Label(parent, SWT.NONE);
@@ -669,7 +669,7 @@ class NewXmlFileCreationPage extends WizardPage {
mRootElementCombo.select(0); mRootElementCombo.select(0);
mRootElementCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mRootElementCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mRootElementCombo.setToolTipText(tooltip); mRootElementCombo.setToolTipText(tooltip);
padWithEmptyCells(parent, 2); padWithEmptyCells(parent, 2);
} }
@@ -683,7 +683,7 @@ class NewXmlFileCreationPage extends WizardPage {
* <li>The current folder, valid if it's a folder under /res</li> * <li>The current folder, valid if it's a folder under /res</li>
* <li>An existing filename, in which case the user will be asked whether to override it.</li> * <li>An existing filename, in which case the user will be asked whether to override it.</li>
* <ul> * <ul>
* *
* @param selection The selection when the wizard was initiated. * @param selection The selection when the wizard was initiated.
*/ */
private void initializeFromSelection(IStructuredSelection selection) { private void initializeFromSelection(IStructuredSelection selection) {
@@ -702,7 +702,7 @@ class NewXmlFileCreationPage extends WizardPage {
if (element instanceof IAdaptable) { if (element instanceof IAdaptable) {
IResource res = (IResource) ((IAdaptable) element).getAdapter(IResource.class); IResource res = (IResource) ((IAdaptable) element).getAdapter(IResource.class);
IProject project = res != null ? res.getProject() : null; IProject project = res != null ? res.getProject() : null;
// Is this an Android project? // Is this an Android project?
try { try {
if (project == null || !project.hasNature(AndroidConstants.NATURE)) { if (project == null || !project.hasNature(AndroidConstants.NATURE)) {
@@ -712,18 +712,18 @@ class NewXmlFileCreationPage extends WizardPage {
// checking the nature failed, ignore this resource // checking the nature failed, ignore this resource
continue; continue;
} }
int score = 1; // we have a valid project at least int score = 1; // we have a valid project at least
IPath wsFolderPath = null; IPath wsFolderPath = null;
String fileName = null; String fileName = null;
if (res.getType() == IResource.FOLDER) { if (res.getType() == IResource.FOLDER) {
wsFolderPath = res.getProjectRelativePath(); wsFolderPath = res.getProjectRelativePath();
} else if (res.getType() == IResource.FILE) { } else if (res.getType() == IResource.FILE) {
fileName = res.getName(); fileName = res.getName();
wsFolderPath = res.getParent().getProjectRelativePath(); wsFolderPath = res.getParent().getProjectRelativePath();
} }
// Disregard this folder selection if it doesn't point to /res/something // Disregard this folder selection if it doesn't point to /res/something
if (wsFolderPath != null && if (wsFolderPath != null &&
wsFolderPath.segmentCount() > 1 && wsFolderPath.segmentCount() > 1 &&
@@ -735,7 +735,7 @@ class NewXmlFileCreationPage extends WizardPage {
} }
score += fileName != null ? 4 : 0; score += fileName != null ? 4 : 0;
if (score > targetScore) { if (score > targetScore) {
targetScore = score; targetScore = score;
targetProject = project; targetProject = project;
@@ -744,7 +744,7 @@ class NewXmlFileCreationPage extends WizardPage {
} }
} }
} }
// Now set the UI accordingly // Now set the UI accordingly
if (targetScore > 0) { if (targetScore > 0) {
mProject = targetProject; mProject = targetProject;
@@ -764,7 +764,7 @@ class NewXmlFileCreationPage extends WizardPage {
if (roots.size() > 0) { if (roots.size() > 0) {
roots.clear(); roots.clear();
} }
// depending of the type of the seed, initialize the root in different ways // depending of the type of the seed, initialize the root in different ways
Object rootSeed = type.getRootSeed(); Object rootSeed = type.getRootSeed();
@@ -783,7 +783,7 @@ class NewXmlFileCreationPage extends WizardPage {
// Note: if project is null, the root list will be empty since it has been // Note: if project is null, the root list will be empty since it has been
// cleared above. // cleared above.
// get the AndroidTargetData from the project // get the AndroidTargetData from the project
IAndroidTarget target = null; IAndroidTarget target = null;
AndroidTargetData data = null; AndroidTargetData data = null;
@@ -793,7 +793,7 @@ class NewXmlFileCreationPage extends WizardPage {
// A project should have a target. The target can be missing if the project // A project should have a target. The target can be missing if the project
// is an old project for which a target hasn't been affected or if the // is an old project for which a target hasn't been affected or if the
// target no longer exists in this SDK. Simply log the error and dismiss. // target no longer exists in this SDK. Simply log the error and dismiss.
AdtPlugin.log(IStatus.INFO, AdtPlugin.log(IStatus.INFO,
"NewXmlFile wizard: no platform target for project %s", //$NON-NLS-1$ "NewXmlFile wizard: no platform target for project %s", //$NON-NLS-1$
mProject.getName()); mProject.getName());
@@ -807,14 +807,14 @@ class NewXmlFileCreationPage extends WizardPage {
// loaded we can end up in a weird case where we have a target but it // loaded we can end up in a weird case where we have a target but it
// doesn't have any data yet. // doesn't have any data yet.
// Lets log a warning and silently ignore this root. // Lets log a warning and silently ignore this root.
AdtPlugin.log(IStatus.INFO, AdtPlugin.log(IStatus.INFO,
"NewXmlFile wizard: no data for target %s, project %s", //$NON-NLS-1$ "NewXmlFile wizard: no data for target %s, project %s", //$NON-NLS-1$
target.getName(), mProject.getName()); target.getName(), mProject.getName());
continue; continue;
} }
} }
IDescriptorProvider provider = data.getDescriptorProvider((Integer)rootSeed); IDescriptorProvider provider = data.getDescriptorProvider((Integer)rootSeed);
ElementDescriptor descriptor = provider.getDescriptor(); ElementDescriptor descriptor = provider.getDescriptor();
if (descriptor != null) { if (descriptor != null) {
@@ -842,22 +842,22 @@ class NewXmlFileCreationPage extends WizardPage {
roots.add(xmlName); roots.add(xmlName);
} }
} }
visited.add(desc); visited.add(desc);
for (ElementDescriptor child : desc.getChildren()) { for (ElementDescriptor child : desc.getChildren()) {
if (!visited.contains(child)) { if (!visited.contains(child)) {
initRootElementDescriptor(roots, child, visited); initRootElementDescriptor(roots, child, visited);
} }
} }
} }
/** /**
* Callback called when the user edits the project text field. * Callback called when the user edits the project text field.
*/ */
private void onProjectFieldUpdated() { private void onProjectFieldUpdated() {
String project = mProjectTextField.getText(); String project = mProjectTextField.getText();
// Is this a valid project? // Is this a valid project?
IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null /*javaModel*/); IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null /*javaModel*/);
IProject found = null; IProject found = null;
@@ -896,15 +896,15 @@ class NewXmlFileCreationPage extends WizardPage {
// enable types based on new API level // enable types based on new API level
enableTypesBasedOnApi(); enableTypesBasedOnApi();
// update the Type with the new descriptors. // update the Type with the new descriptors.
initializeRootValues(); initializeRootValues();
// update the combo // update the combo
updateRootCombo(getSelectedType()); updateRootCombo(getSelectedType());
validatePage(); validatePage();
} }
/** /**
* Callback called when the Folder text field is changed, either programmatically * Callback called when the Folder text field is changed, either programmatically
@@ -929,7 +929,7 @@ class NewXmlFileCreationPage extends WizardPage {
// We get "res/foo" from selections relative to the project when we want a "/res/foo" path. // We get "res/foo" from selections relative to the project when we want a "/res/foo" path.
if (wsFolderPath.startsWith(RES_FOLDER_REL)) { if (wsFolderPath.startsWith(RES_FOLDER_REL)) {
wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length()); wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length());
mInternalWsFolderPathUpdate = true; mInternalWsFolderPathUpdate = true;
mWsFolderPathTextField.setText(wsFolderPath); mWsFolderPathTextField.setText(wsFolderPath);
mInternalWsFolderPathUpdate = false; mInternalWsFolderPathUpdate = false;
@@ -937,7 +937,7 @@ class NewXmlFileCreationPage extends WizardPage {
if (wsFolderPath.startsWith(RES_FOLDER_ABS)) { if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length()); wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR); int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR);
if (pos >= 0) { if (pos >= 0) {
wsFolderPath = wsFolderPath.substring(0, pos); wsFolderPath = wsFolderPath.substring(0, pos);
@@ -985,7 +985,7 @@ class NewXmlFileCreationPage extends WizardPage {
/** /**
* Callback called when one of the type radio button is changed. * Callback called when one of the type radio button is changed.
* *
* @param typeWidget The type radio button that changed. * @param typeWidget The type radio button that changed.
*/ */
private void onRadioTypeUpdated(Button typeWidget) { private void onRadioTypeUpdated(Button typeWidget) {
@@ -1003,13 +1003,13 @@ class NewXmlFileCreationPage extends WizardPage {
break; break;
} }
} }
if (type == null) { if (type == null) {
return; return;
} }
// update the combo // update the combo
updateRootCombo(type); updateRootCombo(type);
// update the folder path // update the folder path
@@ -1022,7 +1022,7 @@ class NewXmlFileCreationPage extends WizardPage {
if (qual == null) { if (qual == null) {
// The configuration is valid. Reformat the folder path using the canonical // The configuration is valid. Reformat the folder path using the canonical
// value from the configuration. // value from the configuration.
newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType()); newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType());
} else { } else {
// The configuration is invalid. We still update the path but this time // The configuration is invalid. We still update the path but this time
@@ -1049,7 +1049,7 @@ class NewXmlFileCreationPage extends WizardPage {
* Helper method that fills the values of the "root element" combo box based * Helper method that fills the values of the "root element" combo box based
* on the currently selected type radio button. Also disables the combo is there's * on the currently selected type radio button. Also disables the combo is there's
* only one choice. Always select the first root element for the given type. * only one choice. Always select the first root element for the given type.
* *
* @param type The currently selected {@link TypeInfo}. Cannot be null. * @param type The currently selected {@link TypeInfo}. Cannot be null.
*/ */
private void updateRootCombo(TypeInfo type) { private void updateRootCombo(TypeInfo type) {
@@ -1059,14 +1059,14 @@ class NewXmlFileCreationPage extends WizardPage {
if (type != null) { if (type != null) {
// get the list of roots. The list can be empty but not null. // get the list of roots. The list can be empty but not null.
ArrayList<String> roots = type.getRoots(); ArrayList<String> roots = type.getRoots();
// enable the combo if there's more than one choice // enable the combo if there's more than one choice
mRootElementCombo.setEnabled(roots != null && roots.size() > 1); mRootElementCombo.setEnabled(roots != null && roots.size() > 1);
for (String root : roots) { for (String root : roots) {
mRootElementCombo.add(root); mRootElementCombo.add(root);
} }
int index = 0; // default is to select the first one int index = 0; // default is to select the first one
String defaultRoot = type.getDefaultRoot(); String defaultRoot = type.getDefaultRoot();
if (defaultRoot != null) { if (defaultRoot != null) {
@@ -1086,16 +1086,16 @@ class NewXmlFileCreationPage extends WizardPage {
} }
TypeInfo type = getSelectedType(); TypeInfo type = getSelectedType();
if (type != null) { if (type != null) {
mConfigSelector.getConfiguration(mTempConfig); mConfigSelector.getConfiguration(mTempConfig);
StringBuffer sb = new StringBuffer(RES_FOLDER_ABS); StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
sb.append(mTempConfig.getFolderName(type.getResFolderType())); sb.append(mTempConfig.getFolderName(type.getResFolderType()));
mInternalWsFolderPathUpdate = true; mInternalWsFolderPathUpdate = true;
mWsFolderPathTextField.setText(sb.toString()); mWsFolderPathTextField.setText(sb.toString());
mInternalWsFolderPathUpdate = false; mInternalWsFolderPathUpdate = false;
validatePage(); validatePage();
} }
} }
@@ -1103,7 +1103,7 @@ class NewXmlFileCreationPage extends WizardPage {
/** /**
* Helper method to select on of the type radio buttons. * Helper method to select on of the type radio buttons.
* *
* @param type The TypeInfo matching the radio button to selected or null to deselect them all. * @param type The TypeInfo matching the radio button to selected or null to deselect them all.
*/ */
private void selectType(TypeInfo type) { private void selectType(TypeInfo type) {
@@ -1130,9 +1130,9 @@ class NewXmlFileCreationPage extends WizardPage {
IAndroidTarget target = mProject != null ? Sdk.getCurrent().getTarget(mProject) : null; IAndroidTarget target = mProject != null ? Sdk.getCurrent().getTarget(mProject) : null;
int currentApiLevel = 1; int currentApiLevel = 1;
if (target != null) { if (target != null) {
currentApiLevel = target.getApiVersionNumber(); currentApiLevel = target.getVersion().getApiLevel();
} }
for (TypeInfo type : sTypes) { for (TypeInfo type : sTypes) {
type.getWidget().setEnabled(type.getTargetApiLevel() <= currentApiLevel); type.getWidget().setEnabled(type.getTargetApiLevel() <= currentApiLevel);
} }
@@ -1175,7 +1175,7 @@ class NewXmlFileCreationPage extends WizardPage {
IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
int currentApiLevel = 1; int currentApiLevel = 1;
if (target != null) { if (target != null) {
currentApiLevel = target.getApiVersionNumber(); currentApiLevel = target.getVersion().getApiLevel();
} }
TypeInfo type = getSelectedType(); TypeInfo type = getSelectedType();

View File

@@ -1,5 +1,14 @@
#!/bin/bash #!/bin/bash
HOST=`uname`
if [ "${HOST:0:6}" == "CYGWIN" ]; then
if [ "x$1" == "x" ] || [ `basename "$1"` != "layoutlib.jar" ]; then
echo "Usage: $0 sdk/platforms/xxx/data/layoutlib.jar"
echo "Argument 1 should be the path to the layoutlib.jar that should be updated by create_bridge_symlinks.sh."
exit 1
fi
fi
echo "### $0 executing" echo "### $0 executing"
function die() { function die() {

View File

@@ -416,7 +416,7 @@ class Main {
mSdkLog.printf(" Name: %s\n", target.getName()); mSdkLog.printf(" Name: %s\n", target.getName());
if (target.isPlatform()) { if (target.isPlatform()) {
mSdkLog.printf(" Type: Platform\n"); mSdkLog.printf(" Type: Platform\n");
mSdkLog.printf(" API level: %d\n", target.getApiVersionNumber()); mSdkLog.printf(" API level: %s\n", target.getVersion().getApiString());
mSdkLog.printf(" Revision: %d\n", target.getRevision()); mSdkLog.printf(" Revision: %d\n", target.getRevision());
} else { } else {
mSdkLog.printf(" Type: Add-On\n"); mSdkLog.printf(" Type: Add-On\n");
@@ -426,7 +426,7 @@ class Main {
mSdkLog.printf(" Description: %s\n", target.getDescription()); mSdkLog.printf(" Description: %s\n", target.getDescription());
} }
mSdkLog.printf(" Based on Android %s (API level %d)\n", mSdkLog.printf(" Based on Android %s (API level %d)\n",
target.getApiVersionName(), target.getApiVersionNumber()); target.getVersionName(), target.getVersion().getApiString());
// display the optional libraries. // display the optional libraries.
IOptionalLibrary[] libraries = target.getOptionalLibraries(); IOptionalLibrary[] libraries = target.getOptionalLibraries();
@@ -501,13 +501,13 @@ class Main {
// get the target of the AVD // get the target of the AVD
IAndroidTarget target = info.getTarget(); IAndroidTarget target = info.getTarget();
if (target.isPlatform()) { if (target.isPlatform()) {
mSdkLog.printf(" Target: %s (API level %d)\n", target.getName(), mSdkLog.printf(" Target: %s (API level %s)\n", target.getName(),
target.getApiVersionNumber()); target.getVersion().getApiString());
} else { } else {
mSdkLog.printf(" Target: %s (%s)\n", target.getName(), target mSdkLog.printf(" Target: %s (%s)\n", target.getName(), target
.getVendor()); .getVendor());
mSdkLog.printf(" Based on Android %s (API level %d)\n", target mSdkLog.printf(" Based on Android %s (API level %s)\n",
.getApiVersionName(), target.getApiVersionNumber()); target.getVersionName(), target.getVersion().getApiString());
} }
// display some extra values. // display some extra values.

View File

@@ -137,14 +137,13 @@ final class AddOnTarget implements IAndroidTarget {
return mDescription; return mDescription;
} }
public String getApiVersionName() { public AndroidVersion getVersion() {
// this is always defined by the base platform // this is always defined by the base platform
return mBasePlatform.getApiVersionName(); return mBasePlatform.getVersion();
} }
public int getApiVersionNumber() { public String getVersionName() {
// this is always defined by the base platform return mBasePlatform.getVersionName();
return mBasePlatform.getApiVersionNumber();
} }
public int getRevision() { public int getRevision() {
@@ -182,7 +181,7 @@ final class AddOnTarget implements IAndroidTarget {
return sampleLoc.getAbsolutePath(); return sampleLoc.getAbsolutePath();
} }
} }
// INTENT FALL-THROUGH // INTENDED FALL-THROUGH
default : default :
return mBasePlatform.getPath(pathId); return mBasePlatform.getPath(pathId);
} }
@@ -222,21 +221,22 @@ final class AddOnTarget implements IAndroidTarget {
// if the receiver has no optional library, then anything with api version number >= to // if the receiver has no optional library, then anything with api version number >= to
// the receiver is compatible. // the receiver is compatible.
if (mLibraries.length == 0) { if (mLibraries.length == 0) {
return target.getApiVersionNumber() >= getApiVersionNumber(); return target.getVersion().getApiLevel() >= getVersion().getApiLevel();
} }
// Otherwise, target is only compatible if the vendor and name are equals with the api // Otherwise, target is only compatible if the vendor and name are equals with the api
// number greater or equal (ie target is a newer version of this add-on). // number greater or equal (ie target is a newer version of this add-on).
if (target.isPlatform() == false) { if (target.isPlatform() == false) {
return (mVendor.equals(target.getVendor()) && mName.equals(target.getName()) && return (mVendor.equals(target.getVendor()) && mName.equals(target.getName()) &&
target.getApiVersionNumber() >= getApiVersionNumber()); target.getVersion().getApiLevel() >= getVersion().getApiLevel());
} }
return false; return false;
} }
public String hashString() { public String hashString() {
return String.format(ADD_ON_FORMAT, mVendor, mName, mBasePlatform.getApiVersionNumber()); return String.format(ADD_ON_FORMAT, mVendor, mName,
mBasePlatform.getVersion().getApiLevel());
} }
@Override @Override
@@ -250,7 +250,7 @@ final class AddOnTarget implements IAndroidTarget {
AddOnTarget addon = (AddOnTarget)obj; AddOnTarget addon = (AddOnTarget)obj;
return mVendor.equals(addon.mVendor) && mName.equals(addon.mName) && return mVendor.equals(addon.mVendor) && mName.equals(addon.mName) &&
mBasePlatform.getApiVersionNumber() == addon.mBasePlatform.getApiVersionNumber(); mBasePlatform.getVersion().getApiLevel() == addon.mBasePlatform.getVersion().getApiLevel();
} }
return super.equals(obj); return super.equals(obj);
@@ -277,7 +277,7 @@ final class AddOnTarget implements IAndroidTarget {
// api version // api version
if (value == 0) { if (value == 0) {
value = getApiVersionNumber() - target.getApiVersionNumber(); value = getVersion().getApiLevel() - target.getVersion().getApiLevel();
} }
return value; return value;

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.sdklib;
import java.util.Properties;
/**
* Represents the version of a target or device.
* <p/>
* A version is defined by an API level and an optional code name.
* <ul><li>Release versions of the Android platform are identified by their API level (integer),
* (technically the code name for release version is "REL" but this class will return
* <code>null<code> instead.)</li>
* <li>Preview versions of the platform are identified by a code name. Their API level
* is usually set to the value of the previous platform.</li></ul>
* <p/>
* While this class contains both values, its goal is to abstract them, so that code comparing 2+
* versions doesn't have to deal with the logic of handle both values.
* <p/>
* There are some cases where ones may want to access the values directly. This can be done
* with {@link #getApiLevel()} and {@link #getCodename()}.
* <p/>
* For generic UI display of the API version, {@link #getApiString()} is to be used.
*
*/
public class AndroidVersion {
private static final String PROP_API_LEVEL = "AndroidVersion.ApiLevel"; //$NON-NLS-1$
private static final String PROP_CODENAME = "AndroidVersion.CodeName"; //$NON-NLS-1$
private final int mApiLevel;
private final String mCodename;
/**
* Creates an {@link AndroidVersion} with the given api level and codename.
*/
public AndroidVersion(int apiLevel, String codename) {
mApiLevel = apiLevel;
mCodename = codename;
}
/**
* Creates an {@link AndroidVersion} from {@link Properties}, with default values if the
* {@link Properties} object doesn't contain the expected values.
* <p/>The {@link Properties} is expected to have been filled with
* {@link #saveProperties(Properties)}.
*/
public AndroidVersion(Properties properties, int defaultApiLevel, String defaultCodeName) {
if (properties == null) {
mApiLevel = defaultApiLevel;
mCodename = defaultCodeName;
} else {
mApiLevel = Integer.parseInt(properties.getProperty(PROP_API_LEVEL,
Integer.toString(defaultApiLevel)));
mCodename = properties.getProperty(PROP_CODENAME, defaultCodeName);
}
}
public void saveProperties(Properties props) {
props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel));
if (mCodename != null) {
props.setProperty(PROP_CODENAME, mCodename);
}
}
/**
* Returns the api level as an integer.
* <p/>For target that are in preview mode, this can be superseded by
* {@link #getCodename()}.
* <p/>To display the API level in the UI, use {@link #getApiString()}, which will use the
* codename if applicable.
* @see #getCodename()
* @see #getApiString()
*/
public int getApiLevel() {
return mApiLevel;
}
/**
* Returns the version code name if applicable, null otherwise.
* <p/>If the codename is non null, then the API level should be ignored, and this should be
* used as a unique identifier of the target instead.
*/
public String getCodename() {
return mCodename;
}
/**
* Returns a string representing the API level and/or the code name.
*/
public String getApiString() {
if (mCodename != null) {
return mCodename;
}
return Integer.toString(mApiLevel);
}
/**
* Returns whether or not the version is a preview version.
*/
public boolean isPreview() {
return mCodename != null;
}
/**
* Checks whether a device running a version similar to the receiver can run a project compiled
* for the given <var>version</var>.
* <p/>
* Be aware that this is not a perfect test, as other properties could break compatibility
* despite this method returning true. For a more comprehensive test, see
* {@link IAndroidTarget#isCompatibleBaseFor(IAndroidTarget)}.
* <p/>
* Nevertheless, when testing if an application can run on a device (where there is no
* access to the list of optional libraries), this method can give a good indication of whether
* there is a chance the application could run, or if there's a direct incompatibility.
*/
public boolean canRun(AndroidVersion appVersion) {
// if the application is compiled for a preview version, the device must be running exactly
// the same.
if (appVersion.mCodename != null) {
return appVersion.mCodename.equals(mCodename);
}
// otherwise, we check the api level (note that a device running a preview version
// will have the api level of the previous platform).
return mApiLevel >= appVersion.mApiLevel;
}
/**
* Returns <code>true</code> if the AndroidVersion is an API level equals to
* <var>apiLevel</var>.
*/
public boolean equals(int apiLevel) {
return mCodename == null && apiLevel == mApiLevel;
}
/**
* Compares the receiver with either an {@link AndroidVersion} object or a {@link String}
* object.
* <p/>If <var>obj</var> is a {@link String}, then the method will first check if it's a string
* representation of a number, in which case it'll compare it to the api level. Otherwise, it'll
* compare it against the code name.
* <p/>For all other type of object give as parameter, this method will return
* <code>false</code>.
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof AndroidVersion) {
AndroidVersion version = (AndroidVersion)obj;
if (mCodename == null) {
return version.mCodename == null &&
mApiLevel == version.mApiLevel;
} else {
return mCodename.equals(version.mCodename) &&
mApiLevel == version.mApiLevel;
}
} else if (obj instanceof String) {
try {
int value = Integer.parseInt((String)obj);
return value == mApiLevel;
} catch (NumberFormatException e) {
// not a number? compare to the code name
return obj.equals(mCodename);
}
}
return false;
}
@Override
public int hashCode() {
if (mCodename != null) {
return mCodename.hashCode();
}
// there may be some collisions between the hashcode of the codename and the api level
// but it's acceptable.
return mApiLevel;
}
}

View File

@@ -119,14 +119,14 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> {
String getDescription(); String getDescription();
/** /**
* Returns the api version as an integer. * Returns the version of the target. This is guaranteed to be non-null.
*/ */
int getApiVersionNumber(); AndroidVersion getVersion();
/** /**
* Returns the platform version as a readable string. * Returns the platform version as a readable string.
*/ */
String getApiVersionName(); public String getVersionName();
/** Returns the revision number for the target. */ /** Returns the revision number for the target. */
int getRevision(); int getRevision();

View File

@@ -26,32 +26,41 @@ import java.util.Map;
*/ */
final class PlatformTarget implements IAndroidTarget { final class PlatformTarget implements IAndroidTarget {
/** String used to get a hash to the platform target */ /** String used to get a hash to the platform target */
private final static String PLATFORM_HASH = "android-%d"; private final static String PLATFORM_HASH_API_LEVEL = "android-%d";
private final static String PLATFORM_HASH_CODENAME = "android-%s";
private final static String PLATFORM_VENDOR = "Android Open Source Project"; private final static String PLATFORM_VENDOR = "Android Open Source Project";
private final static String PLATFORM_NAME = "Android %s"; private final static String PLATFORM_NAME = "Android %s";
private final static String PLATFORM_NAME_PREVIEW = "Android %s (Preview)";
private final String mLocation; private final String mLocation;
private final String mName; private final String mName;
private final int mApiVersionNumber; private final AndroidVersion mVersion;
private final String mApiVersionName; private final String mVersionName;
private final int mRevision; private final int mRevision;
private final Map<String, String> mProperties; private final Map<String, String> mProperties;
private final Map<Integer, String> mPaths = new HashMap<Integer, String>(); private final Map<Integer, String> mPaths = new HashMap<Integer, String>();
private String[] mSkins; private String[] mSkins;
PlatformTarget(String location, Map<String, String> properties, PlatformTarget(String location, Map<String, String> properties,
int apiNumber, String apiName, int revision) { int apiLevel, String codeName, String versionName, int revision) {
mName = String.format(PLATFORM_NAME, apiName);
if (location.endsWith(File.separator) == false) { if (location.endsWith(File.separator) == false) {
location = location + File.separator; location = location + File.separator;
} }
mLocation = location; mLocation = location;
mProperties = Collections.unmodifiableMap(properties); mProperties = Collections.unmodifiableMap(properties);
mApiVersionNumber = apiNumber; mVersion = new AndroidVersion(apiLevel, codeName);
mApiVersionName = apiName; mVersionName = versionName;
mRevision = revision; mRevision = revision;
if (mVersion.isPreview()) {
mName = String.format(PLATFORM_NAME_PREVIEW, mVersionName);
} else {
mName = String.format(PLATFORM_NAME, mVersionName);
}
// pre-build the path to the platform components // pre-build the path to the platform components
mPaths.put(ANDROID_JAR, mLocation + SdkConstants.FN_FRAMEWORK_LIBRARY); mPaths.put(ANDROID_JAR, mLocation + SdkConstants.FN_FRAMEWORK_LIBRARY);
mPaths.put(SOURCES, mLocation + SdkConstants.FD_ANDROID_SOURCES); mPaths.put(SOURCES, mLocation + SdkConstants.FD_ANDROID_SOURCES);
@@ -119,15 +128,15 @@ final class PlatformTarget implements IAndroidTarget {
* @see com.android.sdklib.IAndroidTarget#getDescription() * @see com.android.sdklib.IAndroidTarget#getDescription()
*/ */
public String getDescription() { public String getDescription() {
return String.format("Standard Android platform %s", mApiVersionName); return String.format("Standard Android platform %s", mVersionName);
} }
public int getApiVersionNumber(){ public AndroidVersion getVersion() {
return mApiVersionNumber; return mVersion;
} }
public String getApiVersionName() { public String getVersionName() {
return mApiVersionName; return mVersionName;
} }
public int getRevision() { public int getRevision() {
@@ -189,13 +198,23 @@ final class PlatformTarget implements IAndroidTarget {
return true; return true;
} }
// if the platform has a codename (ie it's a preview of an upcoming platform), then
// both platform must be exactly identical.
if (mVersion.getCodename() != null) {
return equals(target);
}
// target is compatible wit the receiver as long as its api version number is greater or // target is compatible wit the receiver as long as its api version number is greater or
// equal. // equal.
return target.getApiVersionNumber() >= mApiVersionNumber; return target.getVersion().getApiLevel() >= mVersion.getApiLevel();
} }
public String hashString() { public String hashString() {
return String.format(PLATFORM_HASH, mApiVersionNumber); if (mVersion.getCodename() != null) {
return String.format(PLATFORM_HASH_CODENAME, mVersion.getCodename());
}
return String.format(PLATFORM_HASH_API_LEVEL, mVersion.getApiLevel());
} }
@Override @Override
@@ -206,10 +225,12 @@ final class PlatformTarget implements IAndroidTarget {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof PlatformTarget) { if (obj instanceof PlatformTarget) {
return mApiVersionNumber == ((PlatformTarget)obj).mApiVersionNumber; PlatformTarget platform = (PlatformTarget)obj;
return mVersion.equals(platform.getVersion());
} }
return super.equals(obj); return false;
} }
/* /*
@@ -223,7 +244,13 @@ final class PlatformTarget implements IAndroidTarget {
return -1; return -1;
} }
return mApiVersionNumber - target.getApiVersionNumber(); int apiDiff = mVersion.getApiLevel() - target.getVersion().getApiLevel();
if (mVersion.getCodename() != null && apiDiff == 0) {
return mVersion.getCodename().compareTo(target.getVersion().getCodename());
}
return apiDiff;
} }
// ---- platform only methods. // ---- platform only methods.

View File

@@ -42,6 +42,7 @@ import java.util.regex.Pattern;
public final class SdkManager { public final class SdkManager {
public final static String PROP_VERSION_SDK = "ro.build.version.sdk"; public final static String PROP_VERSION_SDK = "ro.build.version.sdk";
public final static String PROP_VERSION_CODENAME = "ro.build.version.codename";
public final static String PROP_VERSION_RELEASE = "ro.build.version.release"; public final static String PROP_VERSION_RELEASE = "ro.build.version.release";
public final static String PROP_VERSION_REVISION = "ro.build.version.incremental"; public final static String PROP_VERSION_REVISION = "ro.build.version.incremental";
@@ -263,23 +264,28 @@ public final class SdkManager {
if (map != null) { if (map != null) {
// look for some specific values in the map. // look for some specific values in the map.
// version string
String apiName = map.get(PROP_VERSION_RELEASE); String apiName = map.get(PROP_VERSION_RELEASE);
if (apiName == null) { if (apiName == null) {
if (log != null) { if (log != null) {
log.error(null, log.error(null,
"Ignoring platform '%1$s': %2$s is missing from '%3$s'", "Ignoring platform '%1$s': %2$s is missing from '%3$s'",
platform.getName(), PROP_VERSION_RELEASE, SdkConstants.FN_BUILD_PROP); platform.getName(), PROP_VERSION_RELEASE,
SdkConstants.FN_BUILD_PROP);
} }
return null; return null;
} }
// api level
int apiNumber; int apiNumber;
String stringValue = map.get(PROP_VERSION_SDK); String stringValue = map.get(PROP_VERSION_SDK);
if (stringValue == null) { if (stringValue == null) {
if (log != null) { if (log != null) {
log.error(null, log.error(null,
"Ignoring platform '%1$s': %2$s is missing from '%3$s'", "Ignoring platform '%1$s': %2$s is missing from '%3$s'",
platform.getName(), PROP_VERSION_SDK, SdkConstants.FN_BUILD_PROP); platform.getName(), PROP_VERSION_SDK,
SdkConstants.FN_BUILD_PROP);
} }
return null; return null;
} else { } else {
@@ -291,19 +297,28 @@ public final class SdkManager {
if (log != null) { if (log != null) {
log.error(null, log.error(null,
"Ignoring platform '%1$s': %2$s is not a valid number in %3$s.", "Ignoring platform '%1$s': %2$s is not a valid number in %3$s.",
platform.getName(), PROP_VERSION_SDK, SdkConstants.FN_BUILD_PROP); platform.getName(), PROP_VERSION_SDK,
SdkConstants.FN_BUILD_PROP);
} }
return null; return null;
} }
} }
// codename (optional)
String apiCodename = map.get(PROP_VERSION_CODENAME);
if (apiCodename != null && apiCodename.equals("REL")) {
apiCodename = null; // REL means it's a release version and therefore the
// codename is irrelevant at this point.
}
int revision = 1; int revision = 1;
stringValue = map.get(PROP_VERSION_REVISION); stringValue = map.get(PROP_VERSION_REVISION);
if (stringValue == null) { if (stringValue == null) {
if (log != null) { if (log != null) {
log.error(null, log.error(null,
"Ignoring platform '%1$s': %2$s is missing from '%3$s'", "Ignoring platform '%1$s': %2$s is missing from '%3$s'",
platform.getName(), PROP_VERSION_REVISION, SdkConstants.FN_BUILD_PROP); platform.getName(), PROP_VERSION_REVISION,
SdkConstants.FN_BUILD_PROP);
} }
return null; return null;
} else { } else {
@@ -346,6 +361,7 @@ public final class SdkManager {
platform.getAbsolutePath(), platform.getAbsolutePath(),
map, map,
apiNumber, apiNumber,
apiCodename,
apiName, apiName,
revision); revision);
@@ -435,8 +451,7 @@ public final class SdkManager {
try { try {
int apiValue = Integer.parseInt(api); int apiValue = Integer.parseInt(api);
for (IAndroidTarget target : targetList) { for (IAndroidTarget target : targetList) {
if (target.isPlatform() && if (target.isPlatform() && target.getVersion().equals(apiValue)) {
target.getApiVersionNumber() == apiValue) {
baseTarget = (PlatformTarget)target; baseTarget = (PlatformTarget)target;
break; break;
} }

View File

@@ -16,6 +16,7 @@
package com.android.sdklib.internal.repository; package com.android.sdklib.internal.repository;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager; import com.android.sdklib.SdkManager;
@@ -36,13 +37,12 @@ import java.util.Properties;
*/ */
public class AddonPackage extends Package { public class AddonPackage extends Package {
private static final String PROP_API_LEVEL = "Addon.ApiLevel"; //$NON-NLS-1$
private static final String PROP_NAME = "Addon.Name"; //$NON-NLS-1$ private static final String PROP_NAME = "Addon.Name"; //$NON-NLS-1$
private static final String PROP_VENDOR = "Addon.Vendor"; //$NON-NLS-1$ private static final String PROP_VENDOR = "Addon.Vendor"; //$NON-NLS-1$
private final String mVendor; private final String mVendor;
private final String mName; private final String mName;
private final int mApiLevel; private final AndroidVersion mVersion;
/** An add-on library. */ /** An add-on library. */
public static class Lib { public static class Lib {
@@ -74,7 +74,10 @@ public class AddonPackage extends Package {
super(source, packageNode, licenses); super(source, packageNode, licenses);
mVendor = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VENDOR); mVendor = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VENDOR);
mName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_NAME); mName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_NAME);
mApiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0); mVersion = new AndroidVersion(
XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0),
null); // add-ons on platform previews is not supported, so the codename is always
// null in this case.
mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkRepository.NODE_LIBS)); mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkRepository.NODE_LIBS));
} }
@@ -88,7 +91,7 @@ public class AddonPackage extends Package {
AddonPackage(IAndroidTarget target, Properties props) { AddonPackage(IAndroidTarget target, Properties props) {
super( null, //source super( null, //source
props, //properties props, //properties
0, //revision target.getRevision(), //revision
null, //license null, //license
target.getDescription(), //description target.getDescription(), //description
null, //descUrl null, //descUrl
@@ -97,7 +100,7 @@ public class AddonPackage extends Package {
target.getLocation() //archiveOsPath target.getLocation() //archiveOsPath
); );
mApiLevel = target.getApiVersionNumber(); mVersion = target.getVersion();
mName = target.getName(); mName = target.getName();
mVendor = target.getVendor(); mVendor = target.getVendor();
@@ -114,13 +117,13 @@ public class AddonPackage extends Package {
/** /**
* Save the properties of the current packages in the given {@link Properties} object. * Save the properties of the current packages in the given {@link Properties} object.
* These properties will later be give the constructor that takes a {@link Properties} object. * These properties will later be given to a constructor that takes a {@link Properties} object.
*/ */
@Override @Override
void saveProperties(Properties props) { void saveProperties(Properties props) {
super.saveProperties(props); super.saveProperties(props);
props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel)); mVersion.saveProperties(props);
props.setProperty(PROP_NAME, mName); props.setProperty(PROP_NAME, mName);
props.setProperty(PROP_VENDOR, mVendor); props.setProperty(PROP_VENDOR, mVendor);
} }
@@ -165,9 +168,9 @@ public class AddonPackage extends Package {
return mName; return mName;
} }
/** Returns the api-level, an int > 0, for platform, add-on and doc packages. */ /** Returns the version, for platform, add-on and doc packages. */
public int getApiLevel() { public AndroidVersion getVersion() {
return mApiLevel; return mVersion;
} }
/** Returns the libs defined in this add-on. Can be an empty array but not null. */ /** Returns the libs defined in this add-on. Can be an empty array but not null. */
@@ -181,7 +184,7 @@ public class AddonPackage extends Package {
return String.format("%1$s by %2$s for Android API %3$d", return String.format("%1$s by %2$s for Android API %3$d",
getName(), getName(),
getVendor(), getVendor(),
getApiLevel()); mVersion.getApiLevel());
} }
/** Returns a long description for an {@link IDescription}. */ /** Returns a long description for an {@link IDescription}. */
@@ -214,7 +217,7 @@ public class AddonPackage extends Package {
// First find if this add-on is already installed. If so, reuse the same directory. // First find if this add-on is already installed. If so, reuse the same directory.
for (IAndroidTarget target : sdkManager.getTargets()) { for (IAndroidTarget target : sdkManager.getTargets()) {
if (!target.isPlatform() && if (!target.isPlatform() &&
target.getApiVersionNumber() == getApiLevel() && target.getVersion().equals(mVersion) &&
target.getName().equals(getName()) && target.getName().equals(getName()) &&
target.getVendor().equals(getVendor())) { target.getVendor().equals(getVendor())) {
return new File(target.getLocation()); return new File(target.getLocation());
@@ -226,7 +229,7 @@ public class AddonPackage extends Package {
String name = suggestedDir; String name = suggestedDir;
if (suggestedDir == null || suggestedDir.length() == 0) { if (suggestedDir == null || suggestedDir.length() == 0) {
name = String.format("addon-%s-%s-%d", getName(), getVendor(), getApiLevel()); //$NON-NLS-1$ name = String.format("addon-%s-%s-%d", getName(), getVendor(), mVersion.getApiLevel()); //$NON-NLS-1$
name = name.toLowerCase(); name = name.toLowerCase();
name = name.replaceAll("[^a-z0-9_-]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$ name = name.replaceAll("[^a-z0-9_-]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
name = name.replaceAll("_+", "_"); //$NON-NLS-1$ //$NON-NLS-2$ name = name.replaceAll("_+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
@@ -268,6 +271,7 @@ public class AddonPackage extends Package {
String newId = newPkg.getName() + "+" + newPkg.getVendor(); //$NON-NLS-1$ String newId = newPkg.getName() + "+" + newPkg.getVendor(); //$NON-NLS-1$
return thisId.equalsIgnoreCase(newId) && return thisId.equalsIgnoreCase(newId) &&
newPkg.getRevision() > this.getRevision(); mVersion.getApiLevel() == newPkg.getVersion().getApiLevel() &&
newPkg.getRevision() > this.getRevision();
} }
} }

View File

@@ -16,6 +16,7 @@
package com.android.sdklib.internal.repository; package com.android.sdklib.internal.repository;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager; import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.repository.Archive.Arch; import com.android.sdklib.internal.repository.Archive.Arch;
@@ -33,9 +34,7 @@ import java.util.Properties;
*/ */
public class DocPackage extends Package { public class DocPackage extends Package {
private static final String PROP_API_LEVEL = "Doc.ApiLevel"; //$NON-NLS-1$ private final AndroidVersion mVersion;
private final int mApiLevel;
/** /**
* Creates a new doc package from the attributes and elements of the given XML node. * Creates a new doc package from the attributes and elements of the given XML node.
@@ -44,7 +43,13 @@ public class DocPackage extends Package {
*/ */
DocPackage(RepoSource source, Node packageNode, Map<String,String> licenses) { DocPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {
super(source, packageNode, licenses); super(source, packageNode, licenses);
mApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_API_LEVEL, 0);
int apiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);
String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_CODENAME);
if (codeName.length() == 0) {
codeName = null;
}
mVersion = new AndroidVersion(apiLevel, codeName);
} }
/** /**
@@ -55,6 +60,7 @@ public class DocPackage extends Package {
DocPackage(RepoSource source, DocPackage(RepoSource source,
Properties props, Properties props,
int apiLevel, int apiLevel,
String codename,
int revision, int revision,
String license, String license,
String description, String description,
@@ -71,8 +77,7 @@ public class DocPackage extends Package {
archiveOs, archiveOs,
archiveArch, archiveArch,
archiveOsPath); archiveOsPath);
mApiLevel = Integer.parseInt( mVersion = new AndroidVersion(props, apiLevel, codename);
getProperty(props, PROP_API_LEVEL, Integer.toString(apiLevel)));
} }
/** /**
@@ -83,20 +88,23 @@ public class DocPackage extends Package {
void saveProperties(Properties props) { void saveProperties(Properties props) {
super.saveProperties(props); super.saveProperties(props);
props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel)); mVersion.saveProperties(props);
} }
/** Returns the api-level, an int > 0, for platform, add-on and doc packages. /** Returns the version, for platform, add-on and doc packages.
* Can be 0 if this is a local package of unknown api-level. */ * Can be 0 if this is a local package of unknown api-level. */
public int getApiLevel() { public AndroidVersion getVersion() {
return mApiLevel; return mVersion;
} }
/** Returns a short description for an {@link IDescription}. */ /** Returns a short description for an {@link IDescription}. */
@Override @Override
public String getShortDescription() { public String getShortDescription() {
if (mApiLevel != 0) { if (mVersion.isPreview()) {
return String.format("Documentation for Android SDK, API %1$d", mApiLevel); return String.format("Documentation for Android '%1$s' Preview SDK",
mVersion.getCodename());
} else if (mVersion.getApiLevel() != 0) {
return String.format("Documentation for Android SDK, API %1$d", mVersion.getApiLevel());
} else { } else {
return String.format("Documentation for Android SDK"); return String.format("Documentation for Android SDK");
} }
@@ -147,6 +155,6 @@ public class DocPackage extends Package {
DocPackage newPkg = (DocPackage) replacementPackage; DocPackage newPkg = (DocPackage) replacementPackage;
return newPkg.getRevision() > this.getRevision() && return newPkg.getRevision() > this.getRevision() &&
newPkg.getApiLevel() >= this.getApiLevel(); newPkg.getVersion().equals(this.getVersion());
} }
} }

View File

@@ -212,6 +212,7 @@ public class LocalSdkParser {
null, //source null, //source
props, //properties props, //properties
0, //apiLevel 0, //apiLevel
null, // codename
0, //revision 0, //revision
null, //license null, //license
null, //description null, //description

View File

@@ -16,6 +16,7 @@
package com.android.sdklib.internal.repository; package com.android.sdklib.internal.repository;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager; import com.android.sdklib.SdkManager;
@@ -34,11 +35,10 @@ import java.util.Properties;
*/ */
public class PlatformPackage extends Package { public class PlatformPackage extends Package {
private static final String PROP_API_LEVEL = "Platform.ApiLevel"; //$NON-NLS-1$
private static final String PROP_VERSION = "Platform.Version"; //$NON-NLS-1$ private static final String PROP_VERSION = "Platform.Version"; //$NON-NLS-1$
private final String mVersion; private final AndroidVersion mVersion;
private final int mApiLevel; private final String mVersionName;
/** /**
* Creates a new platform package from the attributes and elements of the given XML node. * Creates a new platform package from the attributes and elements of the given XML node.
@@ -47,8 +47,13 @@ public class PlatformPackage extends Package {
*/ */
PlatformPackage(RepoSource source, Node packageNode, Map<String,String> licenses) { PlatformPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {
super(source, packageNode, licenses); super(source, packageNode, licenses);
mVersion = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION); mVersionName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION);
mApiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0); int apiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);
String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_CODENAME);
if (codeName.length() == 0) {
codeName = null;
}
mVersion = new AndroidVersion(apiLevel, codeName);
} }
/** /**
@@ -60,7 +65,7 @@ public class PlatformPackage extends Package {
PlatformPackage(IAndroidTarget target, Properties props) { PlatformPackage(IAndroidTarget target, Properties props) {
super( null, //source super( null, //source
props, //properties props, //properties
0, //revision target.getRevision(), //revision
null, //license null, //license
target.getDescription(), //description target.getDescription(), //description
null, //descUrl null, //descUrl
@@ -69,38 +74,43 @@ public class PlatformPackage extends Package {
target.getLocation() //archiveOsPath target.getLocation() //archiveOsPath
); );
mApiLevel = target.getApiVersionNumber(); mVersion = target.getVersion();
mVersion = target.getApiVersionName(); mVersionName = target.getVersionName();
} }
/** /**
* Save the properties of the current packages in the given {@link Properties} object. * Save the properties of the current packages in the given {@link Properties} object.
* These properties will later be give the constructor that takes a {@link Properties} object. * These properties will later be given to a constructor that takes a {@link Properties} object.
*/ */
@Override @Override
void saveProperties(Properties props) { void saveProperties(Properties props) {
super.saveProperties(props); super.saveProperties(props);
props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel)); mVersion.saveProperties(props);
props.setProperty(PROP_VERSION, mVersion); props.setProperty(PROP_VERSION, mVersionName);
} }
/** Returns the version, a string, for platform packages. */ /** Returns the version, a string, for platform packages. */
public String getVersion() { public String getVersionName() {
return mVersion; return mVersionName;
} }
/** Returns the api-level, an int > 0, for platform, add-on and doc packages. */ /** Returns the package version, for platform, add-on and doc packages. */
public int getApiLevel() { public AndroidVersion getVersion() {
return mApiLevel; return mVersion;
} }
/** Returns a short description for an {@link IDescription}. */ /** Returns a short description for an {@link IDescription}. */
@Override @Override
public String getShortDescription() { public String getShortDescription() {
if (mVersion.isPreview()) {
return String.format("SDK Platform Android %1$s (Preview)",
getVersionName());
}
return String.format("SDK Platform Android %1$s, API %2$d", return String.format("SDK Platform Android %1$s, API %2$d",
getVersion(), getVersionName(),
getApiLevel()); mVersion.getApiLevel());
} }
/** Returns a long description for an {@link IDescription}. */ /** Returns a long description for an {@link IDescription}. */
@@ -128,17 +138,17 @@ public class PlatformPackage extends Package {
@Override @Override
public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) { public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {
// First find if this add-on is already installed. If so, reuse the same directory. // First find if this platform is already installed. If so, reuse the same directory.
for (IAndroidTarget target : sdkManager.getTargets()) { for (IAndroidTarget target : sdkManager.getTargets()) {
if (target.isPlatform() && if (target.isPlatform() &&
target.getApiVersionNumber() == getApiLevel() && target.getVersion().equals(mVersion) &&
target.getApiVersionName().equals(getVersion())) { target.getVersionName().equals(getVersionName())) {
return new File(target.getLocation()); return new File(target.getLocation());
} }
} }
File platforms = new File(osSdkRoot, SdkConstants.FD_PLATFORMS); File platforms = new File(osSdkRoot, SdkConstants.FD_PLATFORMS);
File folder = new File(platforms, String.format("android-%s", getVersion())); //$NON-NLS-1$ File folder = new File(platforms, String.format("android-%s", getVersionName())); //$NON-NLS-1$
return folder; return folder;
} }
@@ -162,8 +172,8 @@ public class PlatformPackage extends Package {
} }
PlatformPackage newPkg = (PlatformPackage) replacementPackage; PlatformPackage newPkg = (PlatformPackage) replacementPackage;
return newPkg.getVersion().equalsIgnoreCase(this.getVersion()) && return newPkg.getVersionName().equalsIgnoreCase(this.getVersionName()) &&
newPkg.getApiLevel() == this.getApiLevel() && newPkg.getVersion().equals(this.getVersion()) &&
newPkg.getRevision() > this.getRevision(); newPkg.getRevision() > this.getRevision();
} }
} }

View File

@@ -26,7 +26,7 @@ public class SdkRepository {
/** The URL of the official Google sdk-repository site. */ /** The URL of the official Google sdk-repository site. */
public static final String URL_GOOGLE_SDK_REPO_SITE = public static final String URL_GOOGLE_SDK_REPO_SITE =
"https://dl.google.com/android/repository/"; //$NON-NLS-1$ "https://dl-ssl.google.com/android/repository/"; //$NON-NLS-1$
public static final String URL_DEFAULT_XML_FILE = "repository.xml"; //$NON-NLS-1$ public static final String URL_DEFAULT_XML_FILE = "repository.xml"; //$NON-NLS-1$
@@ -63,6 +63,8 @@ public class SdkRepository {
public static final String NODE_VERSION = "version"; //$NON-NLS-1$ public static final String NODE_VERSION = "version"; //$NON-NLS-1$
/** The api-level, an int > 0, for platform, add-on and doc packages. */ /** The api-level, an int > 0, for platform, add-on and doc packages. */
public static final String NODE_API_LEVEL = "api-level"; //$NON-NLS-1$ public static final String NODE_API_LEVEL = "api-level"; //$NON-NLS-1$
/** The codename, a string, for platform packages. */
public static final String NODE_CODENAME = "codename"; //$NON-NLS-1$
/** The vendor, a string, for add-on packages. */ /** The vendor, a string, for add-on packages. */
public static final String NODE_VENDOR = "vendor"; //$NON-NLS-1$ public static final String NODE_VENDOR = "vendor"; //$NON-NLS-1$
/** The name, a string, for add-on packages or for libraries. */ /** The name, a string, for add-on packages or for libraries. */

View File

@@ -52,6 +52,8 @@
<xsd:element name="version" type="xsd:normalizedString" /> <xsd:element name="version" type="xsd:normalizedString" />
<!-- The Android API Level for the platform. An int > 0. --> <!-- The Android API Level for the platform. An int > 0. -->
<xsd:element name="api-level" type="xsd:positiveInteger" /> <xsd:element name="api-level" type="xsd:positiveInteger" />
<!-- The optional codename for this platform, if it's a preview. -->
<xsd:element name="codename" type="xsd:string" minOccurs="0" />
<!-- The revision, an int > 0, incremented each time a new <!-- The revision, an int > 0, incremented each time a new
package is generated. --> package is generated. -->
@@ -156,6 +158,8 @@
<xsd:all> <xsd:all>
<!-- The Android API Level for the documentation. An int > 0. --> <!-- The Android API Level for the documentation. An int > 0. -->
<xsd:element name="api-level" type="xsd:positiveInteger" /> <xsd:element name="api-level" type="xsd:positiveInteger" />
<!-- The optional codename for this doc, if it's a preview. -->
<xsd:element name="codename" type="xsd:string" minOccurs="0" />
<!-- The revision, an int > 0, incremented each time a new <!-- The revision, an int > 0, incremented each time a new
package is generated. --> package is generated. -->

View File

@@ -16,8 +16,6 @@
package com.android.sdklib.repository; package com.android.sdklib.repository;
import com.android.sdklib.SdkConstants;
import org.xml.sax.ErrorHandler; import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException; import org.xml.sax.SAXParseException;

View File

@@ -156,6 +156,24 @@
</sdk:libs> </sdk:libs>
<sdk:uses-license ref="license2" /> <sdk:uses-license ref="license2" />
</sdk:add-on> </sdk:add-on>
<sdk:platform>
<sdk:version>Pastry</sdk:version>
<sdk:api-level>5</sdk:api-level>
<sdk:codename>Pastry</sdk:codename>
<sdk:revision>3</sdk:revision>
<sdk:uses-license ref="license1" />
<sdk:description>Preview version for Pastry</sdk:description>
<sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>
<!-- The archives node is mandatory and it cannot be empty. -->
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/files/plat1.zip</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:platform>
<sdk:tool> <sdk:tool>
<sdk:revision>1</sdk:revision> <sdk:revision>1</sdk:revision>

View File

@@ -52,7 +52,10 @@ public class AvdManagerPage extends Composite implements ISdkListener {
Label label = new Label(parent, SWT.NONE); Label label = new Label(parent, SWT.NONE);
label.setText("List of existing Android Virtual Devices:"); label.setText("List of existing Android Virtual Devices:");
mAvdSelector = new AvdSelector(parent, mUpdaterData.getAvdManager(), DisplayMode.MANAGER); mAvdSelector = new AvdSelector(parent,
mUpdaterData.getOsSdkRoot(),
mUpdaterData.getAvdManager(),
DisplayMode.MANAGER);
} }
@Override @Override

View File

@@ -23,6 +23,7 @@ import com.android.sdklib.internal.repository.RepoSource;
import com.android.sdklib.internal.repository.RepoSources; import com.android.sdklib.internal.repository.RepoSources;
import com.android.sdklib.repository.SdkRepository; import com.android.sdklib.repository.SdkRepository;
import com.android.sdkuilib.internal.repository.icons.ImageFactory; import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.internal.tasks.ProgressTaskFactory;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.SashForm;

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.sdkuilib.internal.repository; package com.android.sdkuilib.internal.tasks;
import com.android.sdklib.internal.repository.ITaskMonitor; import com.android.sdklib.internal.repository.ITaskMonitor;

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.sdkuilib.internal.repository; package com.android.sdkuilib.internal.tasks;
import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskMonitor; import com.android.sdklib.internal.repository.ITaskMonitor;
@@ -26,7 +26,7 @@ import org.eclipse.swt.widgets.Shell;
/** /**
* An {@link ITaskMonitor} that displays a {@link ProgressDialog}. * An {@link ITaskMonitor} that displays a {@link ProgressDialog}.
*/ */
class ProgressTask implements ITaskMonitor { public final class ProgressTask implements ITaskMonitor {
private static final double MAX_COUNT = 10000.0; private static final double MAX_COUNT = 10000.0;

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.sdkuilib.internal.repository; package com.android.sdkuilib.internal.tasks;
import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskFactory; import com.android.sdklib.internal.repository.ITaskFactory;
@@ -25,7 +25,7 @@ import org.eclipse.swt.widgets.Shell;
* An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog * An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog
* for each new task. * for each new task.
*/ */
public class ProgressTaskFactory implements ITaskFactory { public final class ProgressTaskFactory implements ITaskFactory {
private final Shell mShell; private final Shell mShell;

View File

@@ -19,7 +19,6 @@ package com.android.sdkuilib.internal.widgets;
import com.android.prefs.AndroidLocation; import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkManager; import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
@@ -274,14 +273,14 @@ final class AvdCreationDialog extends Dialog {
for (IAndroidTarget target : sdkManager.getTargets()) { for (IAndroidTarget target : sdkManager.getTargets()) {
String name; String name;
if (target.isPlatform()) { if (target.isPlatform()) {
name = String.format("%s - API Level %d", name = String.format("%s - API Level %s",
target.getName(), target.getName(),
target.getApiVersionNumber()); target.getVersion().getApiString());
} else { } else {
name = String.format("%s (%s) - API Level %d", name = String.format("%s (%s) - API Level %s",
target.getName(), target.getName(),
target.getVendor(), target.getVendor(),
target.getApiVersionNumber()); target.getVersion().getApiString());
} }
mCurrentTargets.put(name, target); mCurrentTargets.put(name, target);
mTargetCombo.add(name); mTargetCombo.add(name);

View File

@@ -16,6 +16,7 @@
package com.android.sdkuilib.internal.widgets; package com.android.sdkuilib.internal.widgets;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
@@ -106,8 +107,9 @@ final class AvdDetailsDialog extends Dialog {
displayValue(c, "Error:", mAvdInfo.getErrorMessage()); displayValue(c, "Error:", mAvdInfo.getErrorMessage());
} else { } else {
IAndroidTarget target = mAvdInfo.getTarget(); IAndroidTarget target = mAvdInfo.getTarget();
displayValue(c, "Target:", String.format("%s (API level %d)", AndroidVersion version = target.getVersion();
target.getName(), target.getApiVersionNumber())); displayValue(c, "Target:", String.format("%s (API level %s)",
target.getName(), version.getApiString()));
// display some extra values. // display some extra values.
Map<String, String> properties = mAvdInfo.getProperties(); Map<String, String> properties = mAvdInfo.getProperties();

View File

@@ -20,10 +20,14 @@ import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog; import com.android.sdklib.ISdkLog;
import com.android.sdklib.NullSdkLog; import com.android.sdklib.NullSdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus; import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus;
import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskMonitor;
import com.android.sdkuilib.internal.repository.icons.ImageFactory; import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.internal.tasks.ProgressTask;
import com.android.sdkuilib.repository.UpdaterWindow; import com.android.sdkuilib.repository.UpdaterWindow;
import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.MessageDialog;
@@ -49,16 +53,19 @@ import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.TableItem;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* The AVD selector is a table that is added to the given parent composite. * The AVD selector is a table that is added to the given parent composite.
* <p/> * <p/>
* To use, create it using {@link #AvdSelector(Composite, AvdManager, DisplayMode)} then * After using one of the constructors, call {@link #setSelection(AvdInfo)},
* call {@link #setSelection(AvdInfo)}, {@link #setSelectionListener(SelectionListener)} * {@link #setSelectionListener(SelectionListener)} and finally use
* and finally use {@link #getSelected()} to retrieve the selection. * {@link #getSelected()} to retrieve the selection.
*/ */
public final class AvdSelector { public final class AvdSelector {
private static int NUM_COL = 2; private static int NUM_COL = 2;
@@ -66,6 +73,7 @@ public final class AvdSelector {
private final DisplayMode mDisplayMode; private final DisplayMode mDisplayMode;
private AvdManager mAvdManager; private AvdManager mAvdManager;
private final String mOsSdkPath;
private Table mTable; private Table mTable;
private Button mDeleteButton; private Button mDeleteButton;
@@ -74,16 +82,20 @@ public final class AvdSelector {
private Button mRefreshButton; private Button mRefreshButton;
private Button mManagerButton; private Button mManagerButton;
private Button mUpdateButton; private Button mUpdateButton;
private Button mStartButton;
private SelectionListener mSelectionListener; private SelectionListener mSelectionListener;
private IAvdFilter mTargetFilter; private IAvdFilter mTargetFilter;
/** Defaults to true. Changed by the {@link #setEnabled(boolean)} method to represent the
* "global" enabled state on this composite. */
private boolean mIsEnabled = true; private boolean mIsEnabled = true;
private ImageFactory mImageFactory; private ImageFactory mImageFactory;
private Image mOkImage; private Image mOkImage;
private Image mBrokenImage; private Image mBrokenImage;
/** /**
* The display mode of the AVD Selector. * The display mode of the AVD Selector.
*/ */
@@ -170,15 +182,20 @@ public final class AvdSelector {
* {@link IAndroidTarget} will be displayed. * {@link IAndroidTarget} will be displayed.
* *
* @param parent The parent composite where the selector will be added. * @param parent The parent composite where the selector will be added.
* @param osSdkPath The SDK root path. When not null, enables the start button to start
* an emulator on a given AVD.
* @param manager the AVD manager. * @param manager the AVD manager.
* @param filter When non-null, will allow filtering the AVDs to display. * @param filter When non-null, will allow filtering the AVDs to display.
* @param extraAction When non-null, displays an extra action button.
* @param displayMode The display mode ({@link DisplayMode}). * @param displayMode The display mode ({@link DisplayMode}).
*
* TODO: pass an ISdkLog and use it when reloading, starting the emulator, etc.
*/ */
public AvdSelector(Composite parent, public AvdSelector(Composite parent,
String osSdkPath,
AvdManager manager, AvdManager manager,
IAvdFilter filter, IAvdFilter filter,
DisplayMode displayMode) { DisplayMode displayMode) {
mOsSdkPath = osSdkPath;
mAvdManager = manager; mAvdManager = manager;
mTargetFilter = filter; mTargetFilter = filter;
mDisplayMode = displayMode; mDisplayMode = displayMode;
@@ -265,6 +282,17 @@ public final class AvdSelector {
} }
}); });
mStartButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
mStartButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mStartButton.setText("Start...");
mStartButton.setToolTipText("Starts the selected AVD.");
mStartButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent arg0) {
onStart();
}
});
Composite padding = new Composite(buttons, SWT.NONE); Composite padding = new Composite(buttons, SWT.NONE);
padding.setLayoutData(new GridData(GridData.FILL_VERTICAL)); padding.setLayoutData(new GridData(GridData.FILL_VERTICAL));
@@ -327,9 +355,11 @@ public final class AvdSelector {
* @param manager the AVD manager. * @param manager the AVD manager.
* @param displayMode The display mode ({@link DisplayMode}). * @param displayMode The display mode ({@link DisplayMode}).
*/ */
public AvdSelector(Composite parent, AvdManager manager, public AvdSelector(Composite parent,
String osSdkPath,
AvdManager manager,
DisplayMode displayMode) { DisplayMode displayMode) {
this(parent, manager, (IAvdFilter)null /* filter */, displayMode); this(parent, osSdkPath, manager, (IAvdFilter)null /* filter */, displayMode);
} }
/** /**
@@ -344,10 +374,11 @@ public final class AvdSelector {
* @param displayMode The display mode ({@link DisplayMode}). * @param displayMode The display mode ({@link DisplayMode}).
*/ */
public AvdSelector(Composite parent, public AvdSelector(Composite parent,
String osSdkPath,
AvdManager manager, AvdManager manager,
IAndroidTarget filter, IAndroidTarget filter,
DisplayMode displayMode) { DisplayMode displayMode) {
this(parent, manager, new TargetBasedFilter(filter), displayMode); this(parent, osSdkPath, manager, new TargetBasedFilter(filter), displayMode);
} }
/** /**
* Sets the table grid layout data. * Sets the table grid layout data.
@@ -373,7 +404,6 @@ public final class AvdSelector {
* This must be called from the UI thread. * This must be called from the UI thread.
* *
* @param reload if true, the AVD manager will reload the AVD from the disk. * @param reload if true, the AVD manager will reload the AVD from the disk.
* @throws AndroidLocationException if reload the AVD failed.
* @return false if the reloading failed. This is always true if <var>reload</var> is * @return false if the reloading failed. This is always true if <var>reload</var> is
* <code>false</code>. * <code>false</code>.
*/ */
@@ -693,8 +723,8 @@ public final class AvdSelector {
IAndroidTarget target = avd.getTarget(); IAndroidTarget target = avd.getTarget();
if (target != null) { if (target != null) {
item.setText(1, target.getFullName()); item.setText(1, target.getFullName());
item.setText(2, target.getApiVersionName()); item.setText(2, target.getVersionName());
item.setText(3, Integer.toString(target.getApiVersionNumber())); item.setText(3, target.getVersion().getApiString());
} else { } else {
item.setText(1, "?"); item.setText(1, "?");
item.setText(2, "?"); item.setText(2, "?");
@@ -735,11 +765,13 @@ public final class AvdSelector {
} }
/** /**
* Updates the enable state of the Details, Delete and Update buttons. * Updates the enable state of the Details, Start, Delete and Update buttons.
*/ */
private void enableActionButtons() { private void enableActionButtons() {
if (mIsEnabled == false) { if (mIsEnabled == false) {
mDetailsButton.setEnabled(false); mDetailsButton.setEnabled(false);
mStartButton.setEnabled(false);
if (mDeleteButton != null) { if (mDeleteButton != null) {
mDeleteButton.setEnabled(false); mDeleteButton.setEnabled(false);
} }
@@ -748,13 +780,18 @@ public final class AvdSelector {
} }
} else { } else {
AvdInfo selection = getTableSelection(); AvdInfo selection = getTableSelection();
boolean hasSelection = selection != null;
mDetailsButton.setEnabled(hasSelection);
mStartButton.setEnabled(mOsSdkPath != null &&
hasSelection &&
selection.getStatus() == AvdStatus.OK);
mDetailsButton.setEnabled(selection != null);
if (mDeleteButton != null) { if (mDeleteButton != null) {
mDeleteButton.setEnabled(selection != null); mDeleteButton.setEnabled(hasSelection);
} }
if (mUpdateButton != null) { if (mUpdateButton != null) {
mUpdateButton.setEnabled(selection != null && mUpdateButton.setEnabled(hasSelection &&
selection.getStatus() == AvdStatus.ERROR_IMAGE_DIR); selection.getStatus() == AvdStatus.ERROR_IMAGE_DIR);
} }
} }
@@ -849,6 +886,113 @@ public final class AvdSelector {
refresh(true /*reload*/); // UpdaterWindow uses its own AVD manager so this one must reload. refresh(true /*reload*/); // UpdaterWindow uses its own AVD manager so this one must reload.
} }
private void onStart() {
AvdInfo avdInfo = getTableSelection();
if (avdInfo == null || mOsSdkPath == null) {
return;
}
String path = mOsSdkPath +
File.separator +
SdkConstants.OS_SDK_TOOLS_FOLDER +
SdkConstants.FN_EMULATOR;
final String avdName = avdInfo.getName();
// build the command line based on the available parameters.
ArrayList<String> list = new ArrayList<String>();
list.add(path);
list.add("-avd"); //$NON-NLS-1$
list.add(avdName);
// convert the list into an array for the call to exec.
final String[] command = list.toArray(new String[list.size()]);
// launch the emulator
new ProgressTask(mTable.getShell(),
"Starting Android Emulator",
new ITask() {
public void run(ITaskMonitor monitor) {
try {
monitor.setDescription("Starting emualator for AVD '%1$s'", avdName);
int n = 10;
monitor.setProgressMax(n);
Process process = Runtime.getRuntime().exec(command);
grabEmulatorOutput(process, monitor);
// This small wait prevents the dialog from closing too fast:
// When it works, the emulator returns immediately, even if no UI
// is shown yet. And when it fails (because the AVD is locked/running)
// if we don't have a wait we don't capture the error for some reason.
for (int i = 0; i < n; i++) {
try {
Thread.sleep(100);
monitor.incProgress(1);
} catch (InterruptedException e) {
// ignore
}
}
} catch (IOException e) {
monitor.setResult("Failed to start emulator: %1$s", e.getMessage());
}
}
});
}
/**
* Get the stderr/stdout outputs of a process and return when the process is done.
* Both <b>must</b> be read or the process will block on windows.
* @param process The process to get the output from.
* @param monitor An {@link ISdkLog} to capture errors.
*/
private void grabEmulatorOutput(final Process process, final ITaskMonitor monitor) {
// read the lines as they come. if null is returned, it's because the process finished
new Thread("emu-stderr") { //$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) {
monitor.setResult("%1$s", line); //$NON-NLS-1$
} else {
break;
}
}
} catch (IOException e) {
// do nothing.
}
}
}.start();
new Thread("emu-stdout") { //$NON-NLS-1$
@Override
public void run() {
InputStreamReader is = new InputStreamReader(process.getInputStream());
BufferedReader outReader = new BufferedReader(is);
try {
while (true) {
String line = outReader.readLine();
if (line != null) {
monitor.setResult("%1$s", line); //$NON-NLS-1$
} else {
break;
}
}
} catch (IOException e) {
// do nothing.
}
}
}.start();
}
/** /**
* Collects all log from the AVD action and displays it in a dialog. * Collects all log from the AVD action and displays it in a dialog.
*/ */

View File

@@ -323,8 +323,8 @@ public class SdkTargetSelector {
item.setData(target); item.setData(target);
item.setText(0, target.getName()); item.setText(0, target.getName());
item.setText(1, target.getVendor()); item.setText(1, target.getVendor());
item.setText(2, target.getApiVersionName()); item.setText(2, target.getVersionName());
item.setText(3, Integer.toString(target.getApiVersionNumber())); item.setText(3, target.getVersion().getApiString());
} }
} else { } else {
table.setEnabled(false); table.setEnabled(false);