diff options
142 files changed, 4361 insertions, 396 deletions
diff --git a/build/product_sdk.mk b/build/product_sdk.mk index 5646668..f8c39fd 100644 --- a/build/product_sdk.mk +++ b/build/product_sdk.mk @@ -40,6 +40,8 @@ PRODUCT_PACKAGES += \ ddms \ ddmuilib \ draw9patch \ + dvlib \ + dvlib-tests \ emulator \ hierarchyviewer \ ide_common \ diff --git a/build/sdk_only_whitelist.mk b/build/sdk_only_whitelist.mk index 3589547..f0acafe 100644 --- a/build/sdk_only_whitelist.mk +++ b/build/sdk_only_whitelist.mk @@ -49,6 +49,7 @@ subdirs += \ sdk/assetstudio \ sdk/common \ sdk/ddms \ + sdk/device_validator \ sdk/eclipse/scripts/rcp \ sdk/hierarchyviewer2 \ sdk/ide_common \ diff --git a/build/tools.atree b/build/tools.atree index 16fdaf5..414f143 100644 --- a/build/tools.atree +++ b/build/tools.atree @@ -108,6 +108,7 @@ framework/lint.jar tools/lib/lint.jar framework/lint_api.jar tools/lib/lint_api.jar framework/lint_checks.jar tools/lib/lint_checks.jar framework/manifmerger.jar tools/lib/manifmerger.jar +framework/dvlib.jar tools/lib/dvlib.jar # 3rd Party java libraries @@ -146,6 +147,13 @@ prebuilts/tools/common/proguard/proguard4.7/bin/proguardgui.sh tools/proguard/bi prebuilts/tools/common/proguard/proguard4.7/bin/retrace.sh tools/proguard/bin/retrace.sh prebuilts/tools/common/proguard/proguard4.7/src/proguard/ant/task.properties tools/proguard/ant/task.properties +# Templates +sdk/templates/projects/NewAndroidApplication tools/templates/projects/NewAndroidApplication +sdk/templates/activities/BlankActivity tools/templates/activities/BlankActivity +sdk/templates/activities/MasterDetailFlow tools/templates/activities/MasterDetailFlow +sdk/templates/other/CustomView tools/templates/other/CustomView +sdk/templates/resources tools/templates/resources + # SDK Controller sdk/apps/SdkController tools/apps/SdkController @@ -163,6 +171,7 @@ framework/common-tests.jar tests/libtests/common-tests.jar framework/sdklib-tests.jar tests/libtests/sdklib-tests.jar framework/sdkuilib-tests.jar tests/libtests/sdkuilib-tests.jar framework/layoutlib_api.jar tests/libtests/layoutlib_api.jar +framework/dvlib-tests.jar tests/libtests/dvlib-tests.jar #FIXME breaks build, manifmerger jar files not properly built #framework/manifmerger-tests.jar tests/libtests/manifmerger-tests.jar diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/AbstractBufferFindTarget.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/AbstractBufferFindTarget.java new file mode 100644 index 0000000..13a787a --- /dev/null +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/AbstractBufferFindTarget.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012 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.ddmuilib; + +import java.util.regex.Pattern; + +/** + * {@link AbstractBufferFindTarget} implements methods to find items inside a buffer. It takes + * care of the logic to search backwards/forwards in the buffer, wrapping around when necessary. + * The actual contents of the buffer should be provided by the classes that extend this. + */ +public abstract class AbstractBufferFindTarget implements IFindTarget { + private int mCurrentSearchIndex; + + // Single element cache of the last search regex + private Pattern mLastSearchPattern; + private String mLastSearchText; + + @Override + public boolean findAndSelect(String text, boolean isNewSearch, boolean searchForward) { + boolean found = false; + int maxIndex = getItemCount(); + + synchronized (this) { + // Find starting index for this search + if (isNewSearch) { + // for new searches, start from an appropriate place as provided by the delegate + mCurrentSearchIndex = getStartingIndex(); + } else { + // for ongoing searches (finding next match for the same term), continue from + // the current result index + mCurrentSearchIndex = getNext(mCurrentSearchIndex, searchForward, maxIndex); + } + + // Create a regex pattern based on the search term. + Pattern pattern; + if (text.equals(mLastSearchText)) { + pattern = mLastSearchPattern; + } else { + pattern = Pattern.compile(text, Pattern.CASE_INSENSITIVE); + mLastSearchPattern = pattern; + mLastSearchText = text; + } + + // Iterate through the list of items. The search ends if we have gone through + // all items once. + int index = mCurrentSearchIndex; + do { + String msgText = getItem(mCurrentSearchIndex); + if (msgText != null && pattern.matcher(msgText).find()) { + found = true; + break; + } + + mCurrentSearchIndex = getNext(mCurrentSearchIndex, searchForward, maxIndex); + } while (index != mCurrentSearchIndex); // loop through entire contents once + } + + if (found) { + selectAndReveal(mCurrentSearchIndex); + } + + return found; + } + + /** Indicate that the log buffer has scrolled by certain number of elements */ + public void scrollBy(int delta) { + synchronized (this) { + if (mCurrentSearchIndex > 0) { + mCurrentSearchIndex = Math.max(0, mCurrentSearchIndex - delta); + } + } + } + + private int getNext(int index, boolean searchForward, int max) { + // increment or decrement index + index = searchForward ? index + 1 : index - 1; + + // take care of underflow + if (index == -1) { + index = max - 1; + } + + // ..and overflow + if (index == max) { + index = 0; + } + + return index; + } + + /** Obtain the number of items in the buffer */ + public abstract int getItemCount(); + + /** Obtain the item at given index */ + public abstract String getItem(int index); + + /** Select and reveal the item at given index */ + public abstract void selectAndReveal(int index); + + /** Obtain the index from which search should begin */ + public abstract int getStartingIndex(); +} diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/FindDialog.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/FindDialog.java new file mode 100644 index 0000000..6370be4 --- /dev/null +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/FindDialog.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 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.ddmuilib; + + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * {@link FindDialog} provides a text box where users can enter text that should be + * searched for in the target editor/view. The buttons "Find Previous" and "Find Next" + * allow users to search forwards/backwards. This dialog simply provides a front end for the user + * and the actual task of searching is delegated to the {@link IFindTarget}. + */ +public class FindDialog extends Dialog { + private Label mStatusLabel; + private Button mFindNext; + private Button mFindPrevious; + private final IFindTarget mTarget; + private Text mSearchText; + private String mPreviousSearchText; + + private final static int FIND_NEXT_ID = IDialogConstants.CLIENT_ID; + private final static int FIND_PREVIOUS_ID = IDialogConstants.CLIENT_ID + 1; + + public FindDialog(Shell shell, IFindTarget target) { + super(shell); + + mTarget = target; + + setShellStyle((getShellStyle() & ~SWT.APPLICATION_MODAL) | SWT.MODELESS); + setBlockOnOpen(true); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite panel = new Composite(parent, SWT.NONE); + panel.setLayout(new GridLayout(2, false)); + panel.setLayoutData(new GridData(GridData.FILL_BOTH)); + + Label lblMessage = new Label(panel, SWT.NONE); + lblMessage.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + lblMessage.setText("Find:"); + + mSearchText = new Text(panel, SWT.BORDER); + mSearchText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + mSearchText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + boolean hasText = !mSearchText.getText().trim().isEmpty(); + mFindNext.setEnabled(hasText); + mFindPrevious.setEnabled(hasText); + } + }); + + mStatusLabel = new Label(panel, SWT.NONE); + mStatusLabel.setForeground(getShell().getDisplay().getSystemColor(SWT.COLOR_DARK_RED)); + GridData gd = new GridData(); + gd.horizontalSpan = 2; + gd.grabExcessHorizontalSpace = true; + mStatusLabel.setLayoutData(gd); + + return panel; + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, false); + mFindNext = createButton(parent, FIND_NEXT_ID, "Find Next", false); + mFindPrevious = createButton(parent, FIND_PREVIOUS_ID, "Find Previous", /* default */ true); + mFindNext.setEnabled(false); + mFindPrevious.setEnabled(false); + } + + @Override + protected void buttonPressed(int buttonId) { + if (buttonId == IDialogConstants.CLOSE_ID) { + close(); + return; + } + + if (buttonId == FIND_PREVIOUS_ID || buttonId == FIND_NEXT_ID) { + if (mTarget != null) { + String searchText = mSearchText.getText(); + boolean newSearch = !searchText.equals(mPreviousSearchText); + mPreviousSearchText = searchText; + boolean searchForward = buttonId == FIND_NEXT_ID; + + boolean hasMatches = mTarget.findAndSelect(searchText, newSearch, searchForward); + if (!hasMatches) { + mStatusLabel.setText("String not found"); + mStatusLabel.pack(); + } else { + mStatusLabel.setText(""); + } + } + } + } +} diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/IFindTarget.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/IFindTarget.java new file mode 100644 index 0000000..9aa6943 --- /dev/null +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/IFindTarget.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2012 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.ddmuilib; + +public interface IFindTarget { + boolean findAndSelect(String text, boolean isNewSearch, boolean searchForward); +} diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java index e2fb22f..e7dcec9 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java @@ -21,9 +21,11 @@ import com.android.ddmlib.IDevice; import com.android.ddmlib.Log.LogLevel; import com.android.ddmuilib.ITableFocusListener; import com.android.ddmuilib.ITableFocusListener.IFocusedTableActivator; +import com.android.ddmuilib.FindDialog; import com.android.ddmuilib.ImageLoader; import com.android.ddmuilib.SelectionDependentPanel; import com.android.ddmuilib.TableHelper; +import com.android.ddmuilib.AbstractBufferFindTarget; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.MenuManager; @@ -908,7 +910,7 @@ public final class LogCatPanel extends SelectionDependentPanel /** Setup menu to be displayed when right clicking a log message. */ private void addRightClickMenu(final Table table) { // This action will pop up a create filter dialog pre-populated with current selection - final Action filterAction = new Action("Filter similar messages..") { + final Action filterAction = new Action("Filter similar messages...") { @Override public void run() { List<LogCatMessage> selectedMessages = getSelectedLogCatMessages(); @@ -922,8 +924,16 @@ public final class LogCatPanel extends SelectionDependentPanel } }; + final Action findAction = new Action("Find...") { + @Override + public void run() { + showFindDialog(); + }; + }; + final MenuManager mgr = new MenuManager(); mgr.add(filterAction); + mgr.add(findAction); final Menu menu = mgr.createContextMenu(table); table.addListener(SWT.MenuDetect, new Listener() { @@ -1172,6 +1182,8 @@ public final class LogCatPanel extends SelectionDependentPanel deletedMessageCount = mDeletedLogCount; mDeletedLogCount = 0; + + mFindTarget.scrollBy(deletedMessageCount); } int originalItemCount = mTable.getItemCount(); @@ -1432,4 +1444,53 @@ public final class LogCatPanel extends SelectionDependentPanel mErrorColor.dispose(); mAssertColor.dispose(); } + + private class LogcatFindTarget extends AbstractBufferFindTarget { + @Override + public void selectAndReveal(int index) { + mTable.deselectAll(); + mTable.select(index); + mTable.showSelection(); + } + + @Override + public int getItemCount() { + return mTable.getItemCount(); + } + + @Override + public String getItem(int index) { + Object data = mTable.getItem(index).getData(); + if (data != null) { + return data.toString(); + } + + return null; + } + + @Override + public int getStartingIndex() { + // start searches from current selection if present, otherwise from the tail end + // of the buffer + int s = mTable.getSelectionIndex(); + if (s != -1) { + return s; + } else { + return getItemCount() - 1; + } + }; + }; + + private FindDialog mFindDialog; + private LogcatFindTarget mFindTarget = new LogcatFindTarget(); + public void showFindDialog() { + if (mFindDialog != null) { + // if the dialog is already displayed + return; + } + + mFindDialog = new FindDialog(Display.getDefault().getActiveShell(), mFindTarget); + mFindDialog.open(); // blocks until find dialog is closed + mFindDialog = null; + } } diff --git a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/RollingBufferFindTest.java b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/RollingBufferFindTest.java new file mode 100644 index 0000000..32a36c4 --- /dev/null +++ b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/RollingBufferFindTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012 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.ddmuilib.logcat; + +import com.android.ddmuilib.AbstractBufferFindTarget; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.List; + +public class RollingBufferFindTest extends TestCase { + public class FindTarget extends AbstractBufferFindTarget { + private int mSelectedItem = -1; + private int mItemReadCount = 0; + private List<String> mItems = Arrays.asList( + "abc", + "def", + "abc", + null, + "xyz" + ); + + @Override + public int getItemCount() { + return mItems.size(); + } + + @Override + public String getItem(int index) { + mItemReadCount++; + return mItems.get(index); + } + + @Override + public void selectAndReveal(int index) { + mSelectedItem = index; + } + + @Override + public int getStartingIndex() { + return mItems.size() - 1; + } + } + FindTarget mFindTarget = new FindTarget(); + + public void testMultipleMatch() { + mFindTarget.mSelectedItem = -1; + + String text = "abc"; + int lastIndex = mFindTarget.mItems.lastIndexOf(text); + int firstIndex = mFindTarget.mItems.indexOf(text); + + // the first time we search through the buffer we should hit the item at lastIndex + assertTrue(mFindTarget.findAndSelect(text, true, false)); + assertEquals(lastIndex, mFindTarget.mSelectedItem); + + // subsequent search should hit the item at first index + assertTrue(mFindTarget.findAndSelect(text, false, false)); + assertEquals(firstIndex, mFindTarget.mSelectedItem); + + // search again should roll over and hit the last index + assertTrue(mFindTarget.findAndSelect(text, false, false)); + assertEquals(lastIndex, mFindTarget.mSelectedItem); + } + + public void testMissingItem() { + mFindTarget.mSelectedItem = -1; + mFindTarget.mItemReadCount = 0; + + // should not match + assertFalse(mFindTarget.findAndSelect("nonexistent", true, false)); + + // no item should be selected + assertEquals(-1, mFindTarget.mSelectedItem); + + // but all items should have been read in once + assertEquals(mFindTarget.getItemCount(), mFindTarget.mItemReadCount); + } + + public void testSearchDirection() { + String text = "abc"; + int lastIndex = mFindTarget.mItems.lastIndexOf(text); + int firstIndex = mFindTarget.mItems.indexOf(text); + + // the first time we search through the buffer we should hit the "abc" from the last + assertTrue(mFindTarget.findAndSelect(text, true, false)); + assertEquals(lastIndex, mFindTarget.mSelectedItem); + + // searching forward from there should also hit the first index + assertTrue(mFindTarget.findAndSelect(text, false, true)); + assertEquals(firstIndex, mFindTarget.mSelectedItem); + } +} diff --git a/device_validator/Android.mk b/device_validator/Android.mk new file mode 100644 index 0000000..9e32461 --- /dev/null +++ b/device_validator/Android.mk @@ -0,0 +1,5 @@ +# Copyright 2012 The Android Open Source Project + +DEVICE_VALIDATOR_LOCAL_DIR := $(call my-dir) +include $(DEVICE_VALIDATOR_LOCAL_DIR)/dvlib/Android.mk +include $(DEVICE_VALIDATOR_LOCAL_DIR)/app/Android.mk diff --git a/device_validator/MODULE_LICENSE_APACHE2 b/device_validator/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/device_validator/MODULE_LICENSE_APACHE2 diff --git a/device_validator/app/.classpath b/device_validator/app/.classpath new file mode 100644 index 0000000..a1d7856 --- /dev/null +++ b/device_validator/app/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry combineaccessrules="false" kind="src" path="/dvlib"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/device_validator/app/.project b/device_validator/app/.project new file mode 100644 index 0000000..fda9e3d --- /dev/null +++ b/device_validator/app/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>device_validator</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/device_validator/app/.settings/org.eclipse.jdt.ui.prefs b/device_validator/app/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..c0fbc32 --- /dev/null +++ b/device_validator/app/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,3 @@ +#Fri May 25 10:41:24 PDT 2012 +eclipse.preferences.version=1 +formatter_settings_version=11 diff --git a/device_validator/app/Android.mk b/device_validator/app/Android.mk new file mode 100644 index 0000000..93e6226 --- /dev/null +++ b/device_validator/app/Android.mk @@ -0,0 +1,31 @@ +# Copyright (C) 2012 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-java-files-under, src) + +LOCAL_JAR_MANIFEST := etc/manifest.txt + +LOCAL_JAVA_LIBRARIES := \ + dvlib + +LOCAL_MODULE := device_validator +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_JAVA_LIBRARY) + +# Build all sub-directories +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/device_validator/app/etc/Android.mk b/device_validator/app/etc/Android.mk new file mode 100644 index 0000000..69a948c --- /dev/null +++ b/device_validator/app/etc/Android.mk @@ -0,0 +1,10 @@ +# Copyright 2012 The Android Open Source Project +# +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_PREBUILT_EXECUTABLES := device_validator +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_PREBUILT) + diff --git a/device_validator/app/etc/README b/device_validator/app/etc/README new file mode 100644 index 0000000..7cf0a0b --- /dev/null +++ b/device_validator/app/etc/README @@ -0,0 +1,19 @@ +Device Validator +================ + The device validator is a simple tool for validating XML that contains + information about a list of Android devices. Simply run the tool from the + command-line with the names of the XML files to validate as the arguments. + If an XML file fails to validate, the errors will be printed to standard + out and the program will terminate with a non-zero exit status. + + See device_example.xml for an example valid XML file, and devices.xsd + for the schema the XML must conform to. + +Usage (Linux/OSX/BSD) +--------------------- + ./device_validator [files...] + +Usage (Windows) +--------------- + (from the untar'd directory) + java -cp . -jar device_validator.jar [files...] diff --git a/device_validator/app/etc/device_validator b/device_validator/app/etc/device_validator new file mode 100755 index 0000000..08a1023 --- /dev/null +++ b/device_validator/app/etc/device_validator @@ -0,0 +1,79 @@ +#!/bin/bash +# Copyright 2012, 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. + +# Set up prog to be the path of this script, including following symlinks, +# and set up progdir to be the fully-qualified pathname of its directory. +prog="$0" +while [ -h "${prog}" ]; do + newProg=`/bin/ls -ld "${prog}"` + newProg=`expr "${newProg}" : ".* -> \(.*\)$"` + if expr "x${newProg}" : 'x/' >/dev/null; then + prog="${newProg}" + else + progdir=`dirname "${prog}"` + prog="${progdir}/${newProg}" + fi +done +oldwd=`pwd` +progdir=`dirname "${prog}"` +cd "${progdir}" +progdir=`pwd` +prog="${progdir}"/`basename "${prog}"` +cd "${oldwd}" + +jarfile=device_validator.jar +frameworkdir="$progdir" +libdir="$progdir" +if [ ! -r "$frameworkdir/$jarfile" ] +then + frameworkdir=`dirname "$progdir"`/tools/lib + libdir=`dirname "$progdir"`/tools/lib +fi +if [ ! -r "$frameworkdir/$jarfile" ] +then + frameworkdir=`dirname "$progdir"`/framework + libdir=`dirname "$progdir"`/lib +fi +if [ ! -r "$frameworkdir/$jarfile" ] +then + echo `basename "$prog"`": can't find $jarfile" + exit 1 +fi + + +# Check args. +if [ debug = "$1" ]; then + # add this in for debugging + java_debug=-agentlib:jdwp=transport=dt_socket,server=y,address=8050,suspend=y + shift 1 +else + java_debug= +fi + +javaCmd="java" + +# Mac OS X needs an additional arg, or you get an "illegal thread" complaint. +if [ `uname` = "Darwin" ]; then + os_opts="-XstartOnFirstThread" +else + os_opts= +fi + +jarpath="$frameworkdir/$jarfile" + +exec "$javaCmd" \ + -Xmx256M $os_opts $java_debug \ + -classpath "$jarpath" \ + com.android.validator.DeviceValidator "$@" diff --git a/device_validator/app/etc/manifest.txt b/device_validator/app/etc/manifest.txt new file mode 100644 index 0000000..55b9038 --- /dev/null +++ b/device_validator/app/etc/manifest.txt @@ -0,0 +1,3 @@ +Main-Class: com.android.validator.DeviceValidator +Class-Path: dvlib.jar + diff --git a/device_validator/app/src/com/android/validator/DeviceValidator.java b/device_validator/app/src/com/android/validator/DeviceValidator.java new file mode 100644 index 0000000..e4038bc --- /dev/null +++ b/device_validator/app/src/com/android/validator/DeviceValidator.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.validator; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +import com.android.dvlib.DeviceSchema; + +public class DeviceValidator { + + public static void main(String[] args) { + if (args.length == 0){ + printHelp(); + System.exit(1); + } + int ret = 0; + for (String a : args) { + File f = (new File(a)).getAbsoluteFile(); + try { + if (!DeviceSchema.validate(new FileInputStream(f), System.err, f.getParentFile())) { + System.err.println("Error validating " + f.getAbsolutePath()); + System.out.println(); + ret = 1; + } else { + System.out.println(f.getAbsolutePath() + " validated successfully."); + } + } catch (FileNotFoundException e) { + System.err.println("File not found: " + a); + ret = 1; + } + } + System.exit(ret); + } + + private static void printHelp() { + System.err.printf("Usage: device_validator [files to validate]...\n"); + } + +} diff --git a/device_validator/dvlib/.classpath b/device_validator/dvlib/.classpath new file mode 100644 index 0000000..88fb9b4 --- /dev/null +++ b/device_validator/dvlib/.classpath @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="tests/src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/device_validator/dvlib/.gitignore b/device_validator/dvlib/.gitignore new file mode 100644 index 0000000..e660fd9 --- /dev/null +++ b/device_validator/dvlib/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/device_validator/dvlib/.project b/device_validator/dvlib/.project new file mode 100644 index 0000000..13abbdf --- /dev/null +++ b/device_validator/dvlib/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>dvlib</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/device_validator/dvlib/Android.mk b/device_validator/dvlib/Android.mk new file mode 100644 index 0000000..1869adb --- /dev/null +++ b/device_validator/dvlib/Android.mk @@ -0,0 +1,26 @@ +# Copyright (C) 2012 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-java-files-under, src) +LOCAL_JAVA_RESOURCE_DIRS := src + +LOCAL_MODULE := dvlib +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_JAVA_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/device_validator/dvlib/src/com/android/dvlib/DeviceSchema.java b/device_validator/dvlib/src/com/android/dvlib/DeviceSchema.java new file mode 100644 index 0000000..101baf4 --- /dev/null +++ b/device_validator/dvlib/src/com/android/dvlib/DeviceSchema.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dvlib; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; + +import javax.xml.XMLConstants; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +public class DeviceSchema { + + public static final String NS_DEVICES_XSD = "http://schemas.android.com/sdk/devices/1"; + + /** + * The "devices" element is the root element of this schema. + * + * It must contain one or more "device" elements that each define the + * hardware, software, and states for a given device. + */ + public static final String NODE_DEVICES = "devices"; + + /** + * A "device" element contains a "hardware" element, a "software" element + * for each API version it supports, and a "state" element for each possible + * state the device could be in. + */ + public static final String NODE_DEVICE = "device"; + + /** + * The "hardware" element contains all of the hardware information for a + * given device. + */ + public static final String NODE_HARDWARE = "hardware"; + + /** + * The "software" element contains all of the software information for an + * API version of the given device. + */ + public static final String NODE_SOFTWARE = "software"; + + /** + * The "state" element contains all of the parameters for a given state of + * the device. It's also capable of redefining hardware configurations if + * they change based on state. + */ + public static final String NODE_STATE = "state"; + + public static final String NODE_KEYBOARD = "keyboard"; + + public static final String NODE_TOUCH = "touch"; + + public static final String NODE_GL_EXTENSIONS = "gl-extensions"; + + public static final String NODE_GL_VERSION = "gl-version"; + + public static final String NODE_NETWORKING = "networking"; + + public static final String NODE_REMOVABLE_STORAGE = "removable-storage"; + + public static final String NODE_FLASH = "flash"; + + public static final String NODE_LIVE_WALLPAPER_SUPPORT = "live-wallpaper-support"; + + public static final String NODE_BUTTONS = "buttons"; + + public static final String NODE_CAMERA = "camera"; + + public static final String NODE_LOCATION = "location"; + + public static final String NODE_GPU = "gpu"; + + public static final String NODE_DOCK = "dock"; + + public static final String NODE_YDPI = "ydpi"; + + public static final String NODE_PLUGGED_IN = "plugged-in"; + + public static final String NODE_Y_DIMENSION = "y-dimension"; + + public static final String NODE_SCREEN_RATIO = "screen-ratio"; + + public static final String NODE_NAV_STATE = "nav-state"; + + public static final String NODE_HAS_MIC = "has-mic"; + + public static final String NODE_RAM = "ram"; + + public static final String NODE_XDPI = "xdpi"; + + public static final String NODE_DIMENSIONS = "dimensions"; + + public static final String NODE_ABI = "abi"; + + public static final String NODE_MECHANISM = "mechanism"; + + public static final String NODE_MULTITOUCH = "multitouch"; + + public static final String NODE_NAV = "nav"; + + public static final String NODE_PIXEL_DENSITY = "pixel-density"; + + public static final String NODE_SCREEN_ORIENTATION = "screen-orientation"; + + public static final String NODE_AUTOFOCUS = "autofocus"; + + public static final String NODE_SCREEN_SIZE = "screen-size"; + + public static final String NODE_DESCRIPTION = "description"; + + public static final String NODE_BLUETOOTH_PROFILES = "bluetooth-profiles"; + + public static final String NODE_SCREEN = "screen"; + + public static final String NODE_SENSORS = "sensors"; + + public static final String NODE_DIAGONAL_LENGTH = "diagonal-length"; + + public static final String NODE_SCREEN_TYPE = "screen-type"; + + public static final String NODE_KEYBOARD_STATE = "keyboard-state"; + + public static final String NODE_X_DIMENSION = "x-dimension"; + + public static final String NODE_CPU = "cpu"; + + public static final String NODE_INTERNAL_STORAGE = "internal-storage"; + + public static final String NODE_META = "meta"; + + public static final String NODE_ICONS = "icons"; + + public static final String NODE_SIXTY_FOUR = "sixty-four"; + + public static final String NODE_SIXTEEN = "sixteen"; + + public static final String NODE_FRAME = "frame"; + + public static final String NODE_PATH = "path"; + + public static final String NODE_PORTRAIT_X_OFFSET = "portrait-x-offset"; + + public static final String NODE_PORTRAIT_Y_OFFSET = "portrait-y-offset"; + + public static final String NODE_LANDSCAPE_X_OFFSET = "landscape-x-offset"; + + public static final String NODE_LANDSCAPE_Y_OFFSET = "landscape-y-offset"; + + public static final String NODE_NAME = "name"; + + public static final String NODE_API_LEVEL = "api-level"; + + public static final String NODE_MANUFACTURER = "manufacturer"; + + public static final String ATTR_DEFAULT = "default"; + + public static final String ATTR_UNIT = "unit"; + + /** + * Validates the input stream. + * + * @param deviceXml + * The XML InputStream to validate. + * @param out + * The OutputStream for error messages. + * @param parent + * The parent directory of the input stream. + * @return Whether the given input constitutes a valid devices file. + */ + public static boolean validate(InputStream deviceXml, OutputStream out, File parent) { + Schema s; + SAXParserFactory factory = SAXParserFactory.newInstance(); + PrintWriter writer = new PrintWriter(out); + try { + s = DeviceSchema.getSchema(); + factory.setValidating(false); + factory.setNamespaceAware(true); + factory.setSchema(s); + ValidationHandler validator = new ValidationHandler(parent, writer); + SAXParser parser = factory.newSAXParser(); + parser.parse(deviceXml, validator); + return validator.isValidDevicesFile(); + } catch (SAXException e) { + writer.println(e.getMessage()); + return false; + } catch (ParserConfigurationException e) { + writer.println("Error creating SAX parser:"); + writer.println(e.getMessage()); + return false; + } catch (IOException e) { + writer.println("Error reading file stream:"); + writer.println(e.getMessage()); + return false; + } finally { + writer.flush(); + } + } + + /** + * Helper to get an input stream of the device config XML schema. + */ + public static InputStream getXsdStream() { + return DeviceSchema.class.getResourceAsStream("devices.xsd"); //$NON-NLS-1$ + } + + /** Helper method that returns a {@link Validator} for our XSD */ + public static Schema getSchema() throws SAXException { + InputStream xsdStream = getXsdStream(); + SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = factory.newSchema(new StreamSource(xsdStream)); + return schema; + } + + /** + * A DefaultHandler that parses only to validate the XML is actually a valid + * devices config, since validation can't be entirely encoded in the devices + * schema. + */ + private static class ValidationHandler extends DefaultHandler { + private boolean mValidDevicesFile = true; + private boolean mDefaultSeen = false; + private String mDeviceName; + private final File mDirectory; + private final PrintWriter mWriter; + private final StringBuilder mStringAccumulator = new StringBuilder(); + + public ValidationHandler(File directory, PrintWriter writer) { + mDirectory = directory; // Possibly null + mWriter = writer; + } + + @Override + public void startElement(String uri, String localName, String name, Attributes attributes) + throws SAXException { + if (NODE_DEVICE.equals(localName)) { + // Reset for a new device + mDefaultSeen = false; + } else if (NODE_STATE.equals(localName)) { + // Check if the state is set to be a default state + String val = attributes.getValue(ATTR_DEFAULT); + if (val != null && ("1".equals(val) || Boolean.parseBoolean(val))) { + /* + * If it is and we already have a default state for this + * device, then the device configuration is invalid. + * Otherwise, set that we've seen a default state for this + * device and continue + */ + + if (mDefaultSeen) { + validationError("More than one default state for device " + mDeviceName); + } else { + mDefaultSeen = true; + } + } + } + mStringAccumulator.setLength(0); + } + + @Override + public void characters(char[] ch, int start, int length) { + mStringAccumulator.append(ch, start, length); + } + + @Override + public void endElement(String uri, String localName, String name) throws SAXException { + // If this is the end of a device node, make sure we have at least + // one default state + if (NODE_DEVICE.equals(localName) && !mDefaultSeen) { + validationError("No default state for device " + mDeviceName); + } else if (NODE_NAME.equals(localName)) { + mDeviceName = mStringAccumulator.toString().trim(); + } else if (NODE_PATH.equals(localName) || NODE_SIXTY_FOUR.equals(localName) + || NODE_SIXTEEN.equals(localName)) { + if (mDirectory == null) { + // There is no given parent directory, so this is not a + // valid devices file + validationError("No parent directory given, but relative paths exist."); + return; + } + // This is going to break on any files that end with a space, + // but that should be an incredibly rare corner case. + String relativePath = mStringAccumulator.toString().trim(); + File f = new File(mDirectory, relativePath); + if (f == null || !f.isFile()) { + validationError(relativePath + " is not a valid path."); + return; + } + String fileName = f.getName(); + int extensionStart = fileName.lastIndexOf("."); + if (extensionStart == -1 || !fileName.substring(extensionStart + 1).equals("png")) { + validationError(relativePath + " is not a valid file type."); + } + } + } + + @Override + public void error(SAXParseException e) { + validationError(e.getMessage()); + } + + @Override + public void fatalError(SAXParseException e) { + validationError(e.getMessage()); + } + + public boolean isValidDevicesFile() { + return mValidDevicesFile; + } + + private void validationError(String reason) { + mWriter.println("Error: " + reason); + mValidDevicesFile = false; + } + + } +} diff --git a/device_validator/dvlib/src/com/android/dvlib/devices.xsd b/device_validator/dvlib/src/com/android/dvlib/devices.xsd new file mode 100644 index 0000000..c3fa482 --- /dev/null +++ b/device_validator/dvlib/src/com/android/dvlib/devices.xsd @@ -0,0 +1,884 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> + +<xsd:schema + targetNamespace="http://schemas.android.com/sdk/devices/1" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:c="http://schemas.android.com/sdk/devices/1" + elementFormDefault="qualified" + attributeFormDefault="unqualified" + version="1"> + + <xsd:element name="devices" type="c:devicesType" /> + + <xsd:complexType name="devicesType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + The "devices" element is the root element of this schema. + + It must contain one or more "device" elements that each define the configurations + and states available for a given device. + </xsd:documentation> + </xsd:annotation> + <xsd:sequence> + <xsd:element name="device" minOccurs="1" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + A device element contains one hardware profile for a device, along with + 1 or more software profiles and 1 or more states. Each software profile + defines the supported software for a given API release, and each state + profile defines a different possible state of the device (screen in + portrait orientation, screen in landscape orientation with the keyboard + out, etc.) + </xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="name" type= "xsd:token" /> + <xsd:element name="manufacturer" type= "xsd:token" /> + <xsd:element name="meta" type= "c:metaType" minOccurs="0" /> + <xsd:element name="hardware" type= "c:hardwareType" /> + <xsd:element name="software" type= "c:softwareType" + maxOccurs="unbounded" /> + <xsd:element name="state" type= "c:stateType" + maxOccurs="unbounded" /> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="hardwareType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + The hardwareType contains all of the hardware information for + a given device. This includes things like the GPU type, screen + size, mic presence, etc. + </xsd:documentation> + </xsd:annotation> + <xsd:sequence> + <xsd:element name="screen" type= "c:screenType" /> + <xsd:element name="networking" type= "c:networkingType" /> + <xsd:element name="sensors" type= "c:sensorsType" /> + <xsd:element name="mic" type= "c:micType" /> + <xsd:element name="camera" type= "c:cameraType" + minOccurs="0" maxOccurs="unbounded" /> + <xsd:element name="keyboard" type= "c:keyboardType" /> + <xsd:element name="nav" type= "c:navType" /> + <xsd:element name="ram" type= "c:ramType" /> + <xsd:element name="buttons" type= "c:buttonsType" /> + <xsd:element name="internal-storage" type= "c:internalStorageType" /> + <xsd:element name="removable-storage" type= "c:removableStorageType" /> + <xsd:element name="cpu" type= "c:cpuType" /> + <xsd:element name="gpu" type= "c:gpuType" /> + <xsd:element name="abi" type= "c:abiType" /> + <xsd:element name="dock" type= "c:dockType" /> + <xsd:element name="plugged-in" type= "c:pluggedInType" /> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="softwareType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + The softwareType contains all of the device's software + information for a given API version. This includes things like + live wallpaper support, OpenGL version, etc. + </xsd:documentation> + </xsd:annotation> + <xsd:sequence> + <xsd:element name="api-level"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies which API version(s) this this element is + defining. This can in the form of a single number + or a range of low to high, separated with a dash and + with either limit missing. The default lower limit is + one, and the default upper limit is unbounded. + The following are valid: + 10 + 7-10 + -10 + 7- + - + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:pattern value="[\d]*-[\d]*|[\d]+" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + <xsd:element name="live-wallpaper-support" type="xsd:boolean"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies whether the device supports live wallpapers. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + + <xsd:element name="bluetooth-profiles"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies all of the available Bluetooth profiles. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:list> + <xsd:simpleType> + <xsd:restriction base="xsd:NMTOKEN"> + <xsd:enumeration value="A2DP" /> + <xsd:enumeration value="ATT" /> + <xsd:enumeration value="AVRCP" /> + <xsd:enumeration value="AVDTP" /> + <xsd:enumeration value="BIP" /> + <xsd:enumeration value="BPP" /> + <xsd:enumeration value="CIP" /> + <xsd:enumeration value="CTP" /> + <xsd:enumeration value="DIP" /> + <xsd:enumeration value="DUN" /> + <xsd:enumeration value="FAX" /> + <xsd:enumeration value="FTP" /> + <xsd:enumeration value="GAVDP" /> + <xsd:enumeration value="GAP" /> + <xsd:enumeration value="GATT" /> + <xsd:enumeration value="GOEP" /> + <xsd:enumeration value="HCRP" /> + <xsd:enumeration value="HDP" /> + <xsd:enumeration value="HFP" /> + <xsd:enumeration value="HID" /> + <xsd:enumeration value="HSP" /> + <xsd:enumeration value="ICP" /> + <xsd:enumeration value="LAP" /> + <xsd:enumeration value="MAP" /> + <xsd:enumeration value="OPP" /> + <xsd:enumeration value="PAN" /> + <xsd:enumeration value="PBA" /> + <xsd:enumeration value="PBAP" /> + <xsd:enumeration value="SPP" /> + <xsd:enumeration value="SDAP" /> + <xsd:enumeration value="SAP" /> + <xsd:enumeration value="SIM" /> + <xsd:enumeration value="rSAP" /> + <xsd:enumeration value="SYNCH" /> + <xsd:enumeration value="VDP" /> + <xsd:enumeration value="WAPB" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:list> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="gl-version"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the OpenGL version supported for this release. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:decimal"> + <xsd:pattern value="[0-9]\.[0-9]" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="gl-extensions"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies all of the supported OpenGL extensions for + this release. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:list itemType="xsd:NMTOKEN" /> + </xsd:simpleType> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="stateType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + The stateType contains the information for a given state of + of the device. States include things like portrait mode, + landscape with the keyboard exposed, etc. States can also + modify the hardware attributes of a device. For instance, if + sliding out the keyboard increased the available screen + real estate, you can define a new screenType to override the + default one defined in the device's hardwareType. + </xsd:documentation> + </xsd:annotation> + <xsd:sequence> + <xsd:element name="description" type="xsd:token"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + A description of the defined state. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + + <xsd:element name="screen-orientation"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Defines the orientation of the screen. Use square if + the device's screen has equal height and width, + otherwise use landscape or portrait. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="portrait" /> + <xsd:enumeration value="landscape" /> + <xsd:enumeration value="square" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="keyboard-state"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Defines the state of the keyboard. If the device has no + keyboard use keysoft, otherwise use keysexposed or keyshidden. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="keyssoft" /> + <xsd:enumeration value="keyshidden" /> + <xsd:enumeration value="keysexposed" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + <xsd:element name="nav-state"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Defines the state of the primary non-touchscreen + navigation hardware on the devices. If the device + doesn't have non-touchscreen navigation hardware use + nonav, otherwise use navexposed or navhidden. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="nonav" /> + <xsd:enumeration value="navhidden" /> + <xsd:enumeration value="navexposed" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + <xsd:element name="screen" type="c:screenType" minOccurs="0" /> + <xsd:element name="networking" type="c:networkingType" + minOccurs="0" /> + <xsd:element name="sensors" type="c:sensorsType" minOccurs="0" /> + <xsd:element name="mic" type="c:micType" minOccurs="0" /> + <xsd:element name="camera" type="c:cameraType" + minOccurs="0" maxOccurs="unbounded" /> + <xsd:element name="keyboard" type="c:keyboardType" minOccurs="0" /> + <xsd:element name="nav" type="c:navType" minOccurs="0" /> + <xsd:element name="ram" type="c:ramType" minOccurs="0" /> + <xsd:element name="buttons" type="c:buttonsType" minOccurs="0" /> + <xsd:element name="internal-storage" type="c:internalStorageType" + minOccurs="0" /> + <xsd:element name="removable-storage" type="c:removableStorageType" + minOccurs="0" /> + <xsd:element name="cpu" type="c:cpuType" minOccurs="0" /> + <xsd:element name="gpu" type="c:gpuType" minOccurs="0" /> + <xsd:element name="abi" type="c:abiType" minOccurs="0" /> + <xsd:element name="dock" type="c:dockType" minOccurs="0" /> + <xsd:element name="plugged-in" type="c:pluggedInType" + minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:token" /> + <xsd:attribute name="default" use="optional" type="xsd:boolean" /> + </xsd:complexType> + + <xsd:complexType name="metaType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Details where more device information can be found, such as + icons and frame images. + </xsd:documentation> + </xsd:annotation> + <xsd:sequence> + <xsd:element name="icons" minOccurs="0"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Contains the relative paths to the icon files for this + device. + </xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="sixty-four" type="xsd:normalizedString"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Relative path for the 64x64 icon. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + <xsd:element name="sixteen" type="xsd:normalizedString" + minOccurs="0"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Relative path for the 16x16 icon. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <xsd:element name="frame" minOccurs="0"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Contains information about the frame for the device. + </xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="path" + type="xsd:normalizedString"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + The relative path to the emulator frame for + the device. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + <xsd:element name="portrait-x-offset" + type="xsd:nonNegativeInteger"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + The offset for the frame in the x direction, + in portrait mode. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + <xsd:element name="portrait-y-offset" + type="xsd:nonNegativeInteger"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + The offset for the frame in the y direction, + in portrait mode. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + <xsd:element name="landscape-x-offset" + type="xsd:nonNegativeInteger"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + The offset for the frame in the x direction, + in landscape mode. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + <xsd:element name="landscape-y-offset" + type="xsd:nonNegativeInteger"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + The offset for the frame in the y direction, + in landscape mode. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="screenType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Contains the specifications for the device's screen. + </xsd:documentation> + </xsd:annotation> + <xsd:sequence> + <xsd:element name="screen-size"> + <xsd:simpleType> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the class of the screen. + </xsd:documentation> + </xsd:annotation> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="small" /> + <xsd:enumeration value="normal" /> + <xsd:enumeration value="large" /> + <xsd:enumeration value="xlarge" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="diagonal-length"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the diagonal length of the screen in inches. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:decimal"> + <!-- Negative lengths are not valid --> + <xsd:minInclusive value="0" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="pixel-density"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the screen density of the device. The + medium density of traditional HVGA screens (mdpi) + is defined to be approximately 160dpi; low density + (ldpi) is 120, and high density (hdpi) is 240. There + is thus a 4:3 scaling factor between each density, + so a 9x9 bitmap in ldpi would be 12x12 in mdpi and + 16x16 in hdpi. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="ldpi" /> + <xsd:enumeration value="mdpi" /> + <xsd:enumeration value="tvdpi" /> + <xsd:enumeration value="hdpi" /> + <xsd:enumeration value="xhdpi" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="screen-ratio"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies whether the configuration is for a taller or + wider than traditional screen. This is based purely on + the aspect ratio of the screen: QVGA, HVGA, and VGA are + notlong; WQVGA, WVGA, FWVGA are long. Note that long may + mean either wide or tall, depending on the current + orientation. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="notlong" /> + <xsd:enumeration value="long" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="dimensions"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the device screen resolution in pixels. + </xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="x-dimension"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the x-dimension's resolution in + pixels. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:positiveInteger" /> + </xsd:simpleType> + </xsd:element> + <xsd:element name="y-dimension"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the y-dimension's resolution in + pixels. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:positiveInteger" /> + </xsd:simpleType> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + + <xsd:element name="xdpi"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the actual density in X of the device screen. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:decimal"> + <!-- Negative DPIs are not valid --> + <xsd:minInclusive value="0" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="ydpi"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the actual density in Y of the device screen. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:decimal"> + <!-- Negative DPIs are not valid --> + <xsd:minInclusive value="0" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="touch"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the touch properties of the device. + </xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="multitouch"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the multitouch capabilities of the + device. This can be none if multitouch is + not supported, basic if the device can track + only basic two finger gestures, distinct if + the device can track two or more fingers + simultaneously, or jazz-hands if the device + can track 5 or more fingers simultaneously. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="none" /> + <xsd:enumeration value="basic" /> + <xsd:enumeration value="distinct" /> + <xsd:enumeration value="jazz-hands" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="mechanism"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the mechanism the device was + created for. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="notouch" /> + <xsd:enumeration value="stylus" /> + <xsd:enumeration value="finger" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="screen-type"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the type of touch screen on the + device. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="notouch" /> + <xsd:enumeration value="capacitive" /> + <xsd:enumeration value="resistive" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + + </xsd:sequence> + </xsd:complexType> + + <xsd:simpleType name="networkingType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the available networking hardware. + </xsd:documentation> + </xsd:annotation> + <xsd:list> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="NFC" /> + <xsd:enumeration value="Bluetooth" /> + <xsd:enumeration value="Wifi" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:list> + </xsd:simpleType> + + <xsd:simpleType name="sensorsType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the available sensors. + </xsd:documentation> + </xsd:annotation> + <xsd:list> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="Accelerometer" /> + <xsd:enumeration value="Barometer" /> + <xsd:enumeration value="Compass" /> + <xsd:enumeration value="GPS" /> + <xsd:enumeration value="Gyroscope" /> + <xsd:enumeration value="LightSensor" /> + <xsd:enumeration value="ProximitySensor" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:list> + </xsd:simpleType> + + <xsd:simpleType name="micType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies whether the device has a mic or not. + </xsd:documentation> + </xsd:annotation> + <xsd:restriction base="xsd:boolean" /> + </xsd:simpleType> + + <xsd:complexType name="cameraType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the attributes of the camera. + </xsd:documentation> + </xsd:annotation> + <xsd:sequence> + <xsd:element name="location"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the location of the camera. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="front" /> + <xsd:enumeration value="back" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <xsd:element name="autofocus" type="xsd:boolean"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies whether the camera can autofocus + </xsd:documentation> + </xsd:annotation> + </xsd:element> + + <xsd:element name="flash" type="xsd:boolean"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies whether the camera has flash. + </xsd:documentation> + </xsd:annotation> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + + <xsd:simpleType name="keyboardType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the type of keyboard on the device. + </xsd:documentation> + </xsd:annotation> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="qwerty" /> + <xsd:enumeration value="12key" /> + <xsd:enumeration value="nokeys" /> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="navType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the primary non-touchscreen navigation + hardware on the device. + </xsd:documentation> + </xsd:annotation> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="dpad" /> + <xsd:enumeration value="trackball" /> + <xsd:enumeration value="wheel" /> + <xsd:enumeration value="nonav" /> + </xsd:restriction> + </xsd:simpleType> + + <xsd:complexType name="ramType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the amount of RAM on the device in the unit provided. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleContent> + <xsd:extension base="xsd:positiveInteger"> + <xsd:attribute name="unit" type="c:storageUnitType" use="required" /> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + + <xsd:simpleType name="buttonsType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies whether the device has physical (hard) buttons + (Home, Search, etc.), or uses soft buttons. + </xsd:documentation> + </xsd:annotation> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="hard" /> + <xsd:enumeration value="soft" /> + </xsd:restriction> + </xsd:simpleType> + + <xsd:complexType name="internalStorageType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + A list specifying the sizes of internal storage in + the device, in the storage size unit provided. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleContent> + <xsd:extension base="c:storageListType"> + <xsd:attribute name="unit" type="c:storageUnitType" + use="required" /> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + + <xsd:complexType name="removableStorageType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the range of available removable storage sizes + in the unit provided. A positive value indicates the device is + available with that storage size included while a zero value + indicates an empty storage slot. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleContent> + <xsd:extension base="c:storageListType"> + <xsd:attribute name="unit" type="c:storageUnitType" + use="required" /> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + + <xsd:simpleType name="storageListType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Defines a list for storage configurations such as internal or + removable storage. A positive value indicates the the device + has a storage unit of that size, while a zero value indicates + there is an empty location for a storage unit (such as an empty + SD card slot). + </xsd:documentation> + </xsd:annotation> + <xsd:list> + <xsd:simpleType> + <xsd:restriction base="xsd:nonNegativeInteger" /> + </xsd:simpleType> + </xsd:list> + </xsd:simpleType> + <xsd:simpleType name="gpuType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the device's GPU. + </xsd:documentation> + </xsd:annotation> + <xsd:restriction base="xsd:token"> + <xsd:minLength value="1" /> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="cpuType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the device's CPU. + </xsd:documentation> + </xsd:annotation> + <xsd:restriction base="xsd:token"> + <xsd:minLength value="1" /> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="abiType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies which ABIs the device conforms to. + </xsd:documentation> + </xsd:annotation> + <xsd:list> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="armeabi" /> + <xsd:enumeration value="armeabi-v7a" /> + <xsd:enumeration value="x86" /> + <xsd:enumeration value="mips" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:list> + </xsd:simpleType> + + <xsd:simpleType name="dockType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the official docks available for the device. + </xsd:documentation> + </xsd:annotation> + <xsd:list> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="desk" /> + <xsd:enumeration value="tv" /> + <xsd:enumeration value="car" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:list> + </xsd:simpleType> + + <xsd:simpleType name="pluggedInType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies when the device is plugged in. + </xsd:documentation> + </xsd:annotation> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="always" /> + <xsd:enumeration value="charge" /> + <xsd:enumeration value="never" /> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="storageUnitType"> + <xsd:annotation> + <xsd:documentation xml:lang="en"> + Specifies the unit of storage. This can be MiB, GiB, etc. + </xsd:documentation> + </xsd:annotation> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="B" /> + <xsd:enumeration value="KiB" /> + <xsd:enumeration value="MiB" /> + <xsd:enumeration value="GiB" /> + <xsd:enumeration value="TiB" /> + </xsd:restriction> + </xsd:simpleType> + +</xsd:schema> diff --git a/device_validator/dvlib/tests/Android.mk b/device_validator/dvlib/tests/Android.mk new file mode 100644 index 0000000..5094d7a --- /dev/null +++ b/device_validator/dvlib/tests/Android.mk @@ -0,0 +1,30 @@ +# Copyright (C) 2012 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-java-files-under, src) +LOCAL_JAVA_RESOURCE_DIRS := src + +LOCAL_MODULE := dvlib-tests +LOCAL_MODULE_TAGS := optional + +LOCAL_JAVA_LIBRARIES := \ + dvlib \ + junit + +include $(BUILD_HOST_JAVA_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/DeviceSchemaTest.java b/device_validator/dvlib/tests/src/com/android/dvlib/DeviceSchemaTest.java new file mode 100644 index 0000000..174f27c --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/DeviceSchemaTest.java @@ -0,0 +1,311 @@ +package com.android.dvlib; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.StringWriter; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import junit.framework.TestCase; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +public class DeviceSchemaTest extends TestCase { + + private void checkFailure(Map<String, String> replacements, String regex) throws Exception { + // Generate XML stream with replacements + InputStream xmlStream = getReplacedStream(replacements); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + URL location = DeviceSchemaTest.class.getResource("."); + File parent = new File(location.toURI()); + assertFalse( + "Validation Assertion Failed, XML failed to validate when it was expected to pass\n", + DeviceSchema.validate(xmlStream, baos, parent)); + assertTrue(String.format("Regex Assertion Failed:\nExpected: %s\nActual: %s\n", regex, baos + .toString().trim()), baos.toString().trim().matches(regex)); + } + + private void checkFailure(String resource, String regex) throws Exception { + URL location = DeviceSchemaTest.class.getResource(resource); + File xml = new File(location.toURI()); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + assertFalse("Validation Assertion Failed, XML validated when it was expected to fail\n", + DeviceSchema.validate(new FileInputStream(xml), baos, xml.getParentFile())); + assertTrue(String.format("Regex Assertion Failed:\nExpected: %s\nActual: %s\n", regex, baos + .toString().trim()), baos.toString().trim().matches(regex)); + } + + private void checkSuccess(Map<String, String> replacements) throws Exception { + InputStream xmlStream = getReplacedStream(replacements); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + URL location = DeviceSchemaTest.class.getResource("."); + File parent = new File(location.toURI()); + assertTrue(DeviceSchema.validate(xmlStream, baos, parent)); + assertTrue(baos.toString().trim().matches("")); + } + + private InputStream getReplacedStream(Map<String, String> replacements) throws Exception { + URL location = DeviceSchema.class.getResource("devices_minimal.xml"); + File xml = new File(location.toURI()); + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + SAXParser parser = factory.newSAXParser(); + ReplacementHandler replacer = new ReplacementHandler(replacements); + parser.parse(xml, replacer); + Document doc = replacer.getGeneratedDocument(); + Transformer tf = TransformerFactory.newInstance().newTransformer(); + // Add indents so we're closer to user generated output + tf.setOutputProperty(OutputKeys.INDENT, "yes"); + DOMSource source = new DOMSource(doc); + StringWriter out = new StringWriter(); + StreamResult result = new StreamResult(out); + tf.transform(source, result); + return new ByteArrayInputStream(out.toString().getBytes("UTF-8")); + } + + public void testValidXml() throws Exception { + URL location = DeviceSchemaTest.class.getResource("devices.xml"); + File xml = new File(location.toURI()); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + boolean result = DeviceSchema.validate(new FileInputStream(xml), baos, xml.getParentFile()); + String output = baos.toString().trim(); + assertTrue( + String.format( + "Validation Assertion Failed, XML failed to validate when it was expected to pass\n%s\n",output), result); + assertTrue(String.format("Regex Assertion Failed\nExpected No Output\nActual: %s\n", baos + .toString().trim()), baos.toString().trim().matches("")); + } + + public void testNoHardware() throws Exception { + String regex = "Error: cvc-complex-type.2.4.a: Invalid content was found starting with " + + "element 'd:software'.*"; + checkFailure("devices_no_hardware.xml", regex); + } + + public void testNoSoftware() throws Exception { + String regex = "Error: cvc-complex-type.2.4.a: Invalid content was found starting with " + + "element 'd:state'.*"; + checkFailure("devices_no_software.xml", regex); + } + + public void testNoDefault() throws Exception { + String regex = "Error: No default state for device Galaxy Nexus.*"; + checkFailure("devices_no_default.xml", regex); + } + + public void testTooManyDefaults() throws Exception { + String regex = "Error: More than one default state for device Galaxy Nexus.*"; + checkFailure("devices_too_many_defaults.xml", regex); + } + + public void testNoStates() throws Exception { + String regex = "Error: cvc-complex-type.2.4.b: The content of element 'd:device' is not " + + "complete.*\nError: No default state for device Galaxy Nexus.*"; + checkFailure("devices_no_states.xml", regex); + } + + public void testBadMechanism() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_MECHANISM, "fanger"); + checkFailure(replacements, "Error: cvc-enumeration-valid: Value 'fanger' is not " + + "facet-valid.*\nError: cvc-type.3.1.3: The value 'fanger' of element " + + "'d:mechanism' is not valid.*"); + } + + public void testNegativeXdpi() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_XDPI, "-1.0"); + checkFailure(replacements, "Error: cvc-minInclusive-valid: Value '-1.0'.*\n" + + "Error: cvc-type.3.1.3: The value '-1.0' of element 'd:xdpi' is not valid.*"); + } + + public void testNegativeYdpi() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_YDPI, "-1"); + checkFailure(replacements, "Error: cvc-minInclusive-valid: Value '-1'.*\n" + + "Error: cvc-type.3.1.3: The value '-1' of element 'd:ydpi' is not valid.*"); + + } + + public void testNegativeDiagonalLength() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_DIAGONAL_LENGTH, "-1.0"); + + checkFailure(replacements, "Error: cvc-minInclusive-valid: Value '-1.0'.*\n" + + "Error: cvc-type.3.1.3: The value '-1.0' of element 'd:diagonal-length'.*"); + + } + + public void testInvalidOpenGLVersion() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_GL_VERSION, "2"); + checkFailure(replacements, "Error: cvc-pattern-valid: Value '2' is not facet-valid.*\n" + + "Error: cvc-type.3.1.3: The value '2' of element 'd:gl-version' is not valid.*"); + } + + public void testInvalidIconTypes() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_SIXTEEN, "extras/sixteen.jpeg"); + replacements.put(DeviceSchema.NODE_SIXTY_FOUR, "extras/sixtyfour.jpeg"); + replacements.put(DeviceSchema.NODE_PATH, "extras/frame.jpeg"); + checkFailure(replacements, "Error: extras/sixtyfour.jpeg is not a valid file type.\n" + + "Error: extras/sixteen.jpeg is not a valid file type.\n" + + "Error: extras/frame.jpeg is not a valid file type."); + } + + public void testMissingIcons() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_SIXTEEN, "extras/missing"); + replacements.put(DeviceSchema.NODE_SIXTY_FOUR, "extras/missing"); + replacements.put(DeviceSchema.NODE_PATH, "extras/missing"); + checkFailure(replacements, "Error: extras/missing is not a valid path.\n" + + "Error: extras/missing is not a valid path.\n" + + "Error: extras/missing is not a valid path."); + } + + public void testEmptyOpenGLExtensions() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_GL_EXTENSIONS, ""); + checkSuccess(replacements); + } + + public void testEmptySensors() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_SENSORS, ""); + checkSuccess(replacements); + } + + public void testEmptyNetworking() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_NETWORKING, ""); + checkSuccess(replacements); + } + + public void testEmptyCpu() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_CPU, ""); + checkFailure(replacements, "Error: cvc-minLength-valid: Value '' with length = '0'.*\n" + + "Error: cvc-type.3.1.3: The value '' of element 'd:cpu' is not valid.*"); + } + + public void testEmptyGpu() throws Exception { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put(DeviceSchema.NODE_GPU, ""); + checkFailure(replacements, "Error: cvc-minLength-valid: Value '' with length = '0'.*\n" + + "Error: cvc-type.3.1.3: The value '' of element 'd:gpu' is not valid.*"); + } + + /** + * Reads in a valid devices XML file and if an element tag is in the + * replacements map, it replaces its text content with the corresponding + * value. Note this has no concept of namespaces or hierarchy, so it will + * replace the contents any and all elements with the specified tag name. + */ + private static class ReplacementHandler extends DefaultHandler { + private Element mCurrElement = null; + private Document mDocument; + private final Stack<Element> mElementStack = new Stack<Element>(); + private final Map<String, String> mPrefixes = new HashMap<String, String>(); + private final Map<String, String> mReplacements; + private final StringBuilder mStringAccumulator = new StringBuilder(); + + public ReplacementHandler(Map<String, String> replacements) { + mReplacements = replacements; + } + + @Override + public void startDocument() { + try { + mDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } catch (ParserConfigurationException e) { + fail(e.getMessage()); + } + } + + @Override + public void startElement(String uri, String localName, String name, Attributes attributes) { + Element element = mDocument.createElement(name); + for (int i = 0; i < attributes.getLength(); i++) { + element.setAttribute(attributes.getQName(i), attributes.getValue(i)); + } + for (String key : mPrefixes.keySet()) { + element.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + key, mPrefixes.get(key)); + } + mPrefixes.clear(); + if (mCurrElement != null) { + mElementStack.push(mCurrElement); + } + mCurrElement = element; + } + + @Override + public void startPrefixMapping(String prefix, String uri) throws SAXException { + mPrefixes.put(prefix, uri); + } + + @Override + public void characters(char[] ch, int start, int length) { + mStringAccumulator.append(ch, start, length); + } + + @Override + public void endElement(String uri, String localName, String name) throws SAXException { + if (mReplacements.containsKey(localName)) { + mCurrElement.appendChild(mDocument.createTextNode(mReplacements.get(localName))); + } else { + String content = mStringAccumulator.toString().trim(); + if (!content.isEmpty()) { + mCurrElement.appendChild(mDocument.createTextNode(content)); + } + } + + if (mElementStack.empty()) { + mDocument.appendChild(mCurrElement); + mCurrElement = null; + } else { + Element parent = mElementStack.pop(); + parent.appendChild(mCurrElement); + mCurrElement = parent; + } + mStringAccumulator.setLength(0); + } + + @Override + public void error(SAXParseException e) { + fail(e.getMessage()); + } + + @Override + public void fatalError(SAXParseException e) { + fail(e.getMessage()); + } + + public Document getGeneratedDocument() { + return mDocument; + } + + } +} diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/devices.xml b/device_validator/dvlib/tests/src/com/android/dvlib/devices.xml new file mode 100644 index 0000000..aeb0f04 --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/devices.xml @@ -0,0 +1,290 @@ +<?xml version="1.0"?> +<d:devices + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:d="http://schemas.android.com/sdk/devices/1"> + + <d:device> + <d:name> + Galaxy Nexus + </d:name> + <d:manufacturer> + Samsung + </d:manufacturer> + <d:meta> + <d:icons> + <d:sixty-four> + extras/sixtyfour.png + </d:sixty-four> + <d:sixteen> + extras/sixteen.png + </d:sixteen> + </d:icons> + <d:frame> + <d:path> + extras/frame.png + </d:path> + <d:portrait-x-offset>0</d:portrait-x-offset> + <d:portrait-y-offset>0</d:portrait-y-offset> + <d:landscape-x-offset>0</d:landscape-x-offset> + <d:landscape-y-offset>0</d:landscape-y-offset> + </d:frame> + </d:meta> + <d:hardware> + <d:screen> + <d:screen-size>normal</d:screen-size> + <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> + <d:pixel-density>xhdpi</d:pixel-density> + <d:screen-ratio>long</d:screen-ratio> + <d:dimensions> + <d:x-dimension>720</d:x-dimension> + <d:y-dimension>1280</d:y-dimension> + </d:dimensions> + <d:xdpi>316</d:xdpi> + <d:ydpi>316</d:ydpi> + <d:touch> + <d:multitouch>jazz-hands</d:multitouch> + <d:mechanism>finger</d:mechanism> + <d:screen-type>capacitive</d:screen-type> + </d:touch> + </d:screen> + <d:networking> + Bluetooth + Wifi + NFC + </d:networking> + <d:sensors> + Accelerometer + Barometer + Gyroscope + Compass + GPS + ProximitySensor + </d:sensors> + <d:mic>true</d:mic> + <d:camera> + <d:location>front</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>false</d:flash> + </d:camera> + <d:camera> + <d:location>back</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>true</d:flash> + </d:camera> + <d:keyboard>nokeys</d:keyboard> + <d:nav>nonav</d:nav> + <d:ram unit="GiB">1</d:ram> + <d:buttons>soft</d:buttons> + <d:internal-storage unit="GiB">16</d:internal-storage> + <d:removable-storage unit="KiB"></d:removable-storage> + <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> + <d:gpu>PowerVR SGX540</d:gpu> + <d:abi> + armeabi + armeabi-v7a + </d:abi> + <!--dock (car, desk, tv, none)--> + <d:dock> + </d:dock> + <!-- plugged in (never, charge, always) --> + <d:plugged-in>charge</d:plugged-in> + </d:hardware> + <d:software> + <d:api-level>14-</d:api-level> + <d:live-wallpaper-support>true</d:live-wallpaper-support> + <d:bluetooth-profiles> + HSP + HFP + SPP + A2DP + AVRCP + OPP + PBAP + GAVDP + AVDTP + HID + HDP + PAN + </d:bluetooth-profiles> + <d:gl-version>2.0</d:gl-version> + <!-- + These can be gotten via + javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); + --> + <d:gl-extensions> + GL_EXT_discard_framebuffer + GL_EXT_multi_draw_arrays + GL_EXT_shader_texture_lod + GL_EXT_texture_format_BGRA8888 + GL_IMG_multisampled_render_to_texture + GL_IMG_program_binary + GL_IMG_read_format + GL_IMG_shader_binary + GL_IMG_texture_compression_pvrtc + GL_IMG_texture_format_BGRA8888 + GL_IMG_texture_npot + GL_OES_compressed_ETC1_RGB8_texture + GL_OES_depth_texture + GL_OES_depth24 + GL_OES_EGL_image + GL_OES_EGL_image_external + GL_OES_egl_sync + GL_OES_element_index_uint + GL_OES_fragment_precision_high + GL_OES_get_program_binary + GL_OES_mapbuffer + GL_OES_packed_depth_stencil + GL_OES_required_internalformat + GL_OES_rgb8_rgba8 + GL_OES_standard_derivatives + GL_OES_texture_float + GL_OES_texture_half_float + GL_OES_vertex_array_object + GL_OES_vertex_half_float + </d:gl-extensions> + </d:software> + <d:state name="Portrait" default="true"> + <d:description>The phone in portrait view</d:description> + <d:screen-orientation>portrait</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + <d:state name="Landscape"> + <d:description>The phone in landscape view</d:description> + <d:screen-orientation>landscape</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + </d:device> + <d:device> + <d:name>Droid</d:name> + <d:manufacturer>Motorola</d:manufacturer> + <d:hardware> + <d:screen> + <d:screen-size>normal</d:screen-size> + <d:diagonal-length>3.7</d:diagonal-length> + <d:pixel-density>hdpi</d:pixel-density> + <d:screen-ratio>long</d:screen-ratio> + <d:dimensions> + <d:x-dimension>480</d:x-dimension> + <d:y-dimension>854</d:y-dimension> + </d:dimensions> + <d:xdpi>265</d:xdpi> + <d:ydpi>265</d:ydpi> + <d:touch> + <d:multitouch>distinct</d:multitouch> + <d:mechanism>finger</d:mechanism> + <d:screen-type>capacitive</d:screen-type> + </d:touch> + </d:screen> + <d:networking> + Bluetooth + Wifi + NFC + </d:networking> + <d:sensors> + Accelerometer + Barometer + Compass + GPS + ProximitySensor + LightSensor + </d:sensors> + <d:mic>true</d:mic> + <d:camera> + <d:location>back</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>true</d:flash> + </d:camera> + <d:keyboard>qwerty</d:keyboard> + <d:nav>dpad</d:nav> + <d:ram unit="MiB">256</d:ram> + <d:buttons>hard</d:buttons> + <d:internal-storage unit="MiB">512</d:internal-storage> + <d:removable-storage unit="GiB">16</d:removable-storage> + <d:cpu>OMAP 3430</d:cpu> + <d:gpu>PowerVR SGX 53</d:gpu> + <d:abi> + armeabi + armeabi-v7a + </d:abi> + <d:dock> + car + desk + </d:dock> + <d:plugged-in>charge</d:plugged-in> + </d:hardware> + <d:software> + <d:api-level>5-8</d:api-level> + <d:live-wallpaper-support>false</d:live-wallpaper-support> + <d:bluetooth-profiles> + GAP + SPP + HSP + HFP + A2DP + AVRCP + SDAP + </d:bluetooth-profiles> + <d:gl-version>1.1</d:gl-version> + <!-- + These can be gotten via + javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); + --> + <d:gl-extensions> + GL_OES_byte_coordinates + GL_OES_fixed_point + GL_OES_single_precision + GL_OES_matrix_get + GL_OES_read_format + GL_OES_compressed_paletted_texture + GL_OES_point_sprite + GL_OES_point_size_array + GL_OES_matrix_palette + GL_OES_draw_texture + GL_OES_query_matrix + GL_OES_texture_env_crossbar + GL_OES_texture_mirrored_repeat + GL_OES_texture_cube_map + GL_OES_blend_subtract + GL_OES_blend_func_separate + GL_OES_blend_equation_separate + GL_OES_stencil_wrap + GL_OES_extended_matrix_palette + GL_OES_framebuffer_object + GL_OES_rgb8_rgba8 + GL_OES_depth24 + GL_OES_stencil8 + GL_OES_compressed_ETC1_RGB8_texture + GL_OES_mapbuffer + GL_OES_EGL_image + GL_EXT_multi_draw_arrays + GL_OES_required_internalformat + GL_IMG_read_format + GL_IMG_texture_compression_pvrtc + GL_IMG_texture_format_BGRA8888 + GL_EXT_texture_format_BGRA8888 + GL_IMG_texture_stream + GL_IMG_vertex_program + </d:gl-extensions> + </d:software> + <d:state name="Portrait" default="true"> + <d:description>The phone in portrait view</d:description> + <d:screen-orientation>portrait</d:screen-orientation> + <d:keyboard-state>keyshidden</d:keyboard-state> + <d:nav-state>navhidden</d:nav-state> + </d:state> + <d:state name="Landscape, closed"> + <d:description>The phone in landscape view with the keyboard closed</d:description> + <d:screen-orientation>landscape</d:screen-orientation> + <d:keyboard-state>keyshidden</d:keyboard-state> + <d:nav-state>navhidden</d:nav-state> + </d:state> + <d:state name="Landscape, open"> + <d:description>The phone in landscape view with the keyboard open</d:description> + <d:screen-orientation>landscape</d:screen-orientation> + <d:keyboard-state>keysexposed</d:keyboard-state> + <d:nav-state>navexposed</d:nav-state> + </d:state> + </d:device> +</d:devices> diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/devices_minimal.xml b/device_validator/dvlib/tests/src/com/android/dvlib/devices_minimal.xml new file mode 100644 index 0000000..71ab61a --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/devices_minimal.xml @@ -0,0 +1,155 @@ +<?xml version="1.0"?> +<d:devices + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:d="http://schemas.android.com/sdk/devices/1"> + + <d:device> + <d:name> + Galaxy Nexus + </d:name> + <d:manufacturer> + Samsung + </d:manufacturer> + <d:meta> + <d:icons> + <d:sixty-four> + extras/sixtyfour.png + </d:sixty-four> + <d:sixteen> + extras/sixteen.png + </d:sixteen> + </d:icons> + <d:frame> + <d:path> + extras/frame.png + </d:path> + <d:portrait-x-offset>0</d:portrait-x-offset> + <d:portrait-y-offset>0</d:portrait-y-offset> + <d:landscape-x-offset>0</d:landscape-x-offset> + <d:landscape-y-offset>0</d:landscape-y-offset> + </d:frame> + </d:meta> + <d:hardware> + <d:screen> + <d:screen-size>normal</d:screen-size> + <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> + <d:pixel-density>xhdpi</d:pixel-density> + <d:screen-ratio>long</d:screen-ratio> + <d:dimensions> + <d:x-dimension>720</d:x-dimension> + <d:y-dimension>1280</d:y-dimension> + </d:dimensions> + <d:xdpi>316</d:xdpi> + <d:ydpi>316</d:ydpi> + <d:touch> + <d:multitouch>jazz-hands</d:multitouch> + <d:mechanism>finger</d:mechanism> + <d:screen-type>capacitive</d:screen-type> + </d:touch> + </d:screen> + <d:networking> + Bluetooth + Wifi + NFC + </d:networking> + <d:sensors> + Accelerometer + Barometer + Gyroscope + Compass + GPS + ProximitySensor + </d:sensors> + <d:mic>true</d:mic> + <d:camera> + <d:location>front</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>false</d:flash> + </d:camera> + <d:camera> + <d:location>back</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>true</d:flash> + </d:camera> + <d:keyboard>nokeys</d:keyboard> + <d:nav>nonav</d:nav> + <d:ram unit="GiB">1</d:ram> + <d:buttons>soft</d:buttons> + <d:internal-storage unit="GiB">16</d:internal-storage> + <d:removable-storage unit="KiB"></d:removable-storage> + <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> + <d:gpu>PowerVR SGX540</d:gpu> + <d:abi> + armeabi + armeabi-v7a + </d:abi> + <!--dock (car, desk, tv, none)--> + <d:dock> + </d:dock> + <!-- plugged in (never, charge, always) --> + <d:plugged-in>charge</d:plugged-in> + </d:hardware> + <d:software> + <d:api-level>15</d:api-level> + <d:live-wallpaper-support>true</d:live-wallpaper-support> + <d:bluetooth-profiles> + HSP + HFP + SPP + A2DP + AVRCP + OPP + PBAP + GAVDP + AVDTP + HID + HDP + PAN + </d:bluetooth-profiles> + <d:gl-version>2.0</d:gl-version> + <d:gl-extensions> + GL_EXT_discard_framebuffer + GL_EXT_multi_draw_arrays + GL_EXT_shader_texture_lod + GL_EXT_texture_format_BGRA8888 + GL_IMG_multisampled_render_to_texture + GL_IMG_program_binary + GL_IMG_read_format + GL_IMG_shader_binary + GL_IMG_texture_compression_pvrtc + GL_IMG_texture_format_BGRA8888 + GL_IMG_texture_npot + GL_OES_compressed_ETC1_RGB8_texture + GL_OES_depth_texture + GL_OES_depth24 + GL_OES_EGL_image + GL_OES_EGL_image_external + GL_OES_egl_sync + GL_OES_element_index_uint + GL_OES_fragment_precision_high + GL_OES_get_program_binary + GL_OES_mapbuffer + GL_OES_packed_depth_stencil + GL_OES_required_internalformat + GL_OES_rgb8_rgba8 + GL_OES_standard_derivatives + GL_OES_texture_float + GL_OES_texture_half_float + GL_OES_vertex_array_object + GL_OES_vertex_half_float + </d:gl-extensions> + </d:software> + <d:state name="Portrait" default="true"> + <d:description>The phone in portrait view</d:description> + <d:screen-orientation>portrait</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + <d:state name="Landscape"> + <d:description>The phone in landscape view</d:description> + <d:screen-orientation>landscape</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + </d:device> +</d:devices> diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_default.xml b/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_default.xml new file mode 100644 index 0000000..3ebcedb --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_default.xml @@ -0,0 +1,134 @@ +<?xml version="1.0"?> +<d:devices + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:d="http://schemas.android.com/sdk/devices/1"> + + <d:device> + <d:name> + Galaxy Nexus + </d:name> + <d:manufacturer> + Samsung + </d:manufacturer> + <d:hardware> + <d:screen> + <d:screen-size>normal</d:screen-size> + <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> + <d:pixel-density>xhdpi</d:pixel-density> + <d:screen-ratio>long</d:screen-ratio> + <d:dimensions> + <d:x-dimension>720</d:x-dimension> + <d:y-dimension>1280</d:y-dimension> + </d:dimensions> + <d:xdpi>316</d:xdpi> + <d:ydpi>316</d:ydpi> + <d:touch> + <d:multitouch>jazz-hands</d:multitouch> + <d:mechanism>finger</d:mechanism> + <d:screen-type>capacitive</d:screen-type> + </d:touch> + </d:screen> + <d:networking> + Bluetooth + Wifi + NFC + </d:networking> + <d:sensors> + Accelerometer + Barometer + Gyroscope + Compass + GPS + ProximitySensor + </d:sensors> + <d:mic>true</d:mic> + <d:camera> + <d:location>front</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>false</d:flash> + </d:camera> + <d:camera> + <d:location>back</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>true</d:flash> + </d:camera> + <d:keyboard>nokeys</d:keyboard> + <d:nav>nonav</d:nav> + <d:ram unit="GiB">1</d:ram> + <d:buttons>soft</d:buttons> + <d:internal-storage unit="GiB">16</d:internal-storage> + <d:removable-storage unit="KiB"></d:removable-storage> + <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> + <d:gpu>PowerVR SGX540</d:gpu> + <d:abi> + armeabi + armeabi-v7a + </d:abi> + <!--dock (car, desk, tv, none)--> + <d:dock> + </d:dock> + <!-- plugged in (never, charge, always) --> + <d:plugged-in>charge</d:plugged-in> + </d:hardware> + <d:software> + <d:api-level>14</d:api-level> + <d:live-wallpaper-support>true</d:live-wallpaper-support> + <d:bluetooth-profiles> + HSP + HFP + SPP + A2DP + AVRCP + OPP + PBAP + GAVDP + AVDTP + HID + HDP + PAN + </d:bluetooth-profiles> + <d:gl-version>2.0</d:gl-version> + <!-- + These can be gotten via + javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); + --> + <d:gl-extensions> + GL_EXT_discard_framebuffer + GL_EXT_multi_draw_arrays + GL_EXT_shader_texture_lod + GL_EXT_texture_format_BGRA8888 + GL_IMG_multisampled_render_to_texture + GL_IMG_program_binary + GL_IMG_read_format + GL_IMG_shader_binary + GL_IMG_texture_compression_pvrtc + GL_IMG_texture_format_BGRA8888 + GL_IMG_texture_npot + GL_OES_compressed_ETC1_RGB8_texture + GL_OES_depth_texture + GL_OES_depth24 + GL_OES_EGL_image + GL_OES_EGL_image_external + GL_OES_egl_sync + GL_OES_element_index_uint + GL_OES_fragment_precision_high + GL_OES_get_program_binary + GL_OES_mapbuffer + GL_OES_packed_depth_stencil + GL_OES_required_internalformat + GL_OES_rgb8_rgba8 + GL_OES_standard_derivatives + GL_OES_texture_float + GL_OES_texture_half_float + GL_OES_vertex_array_object + GL_OES_vertex_half_float + </d:gl-extensions> + </d:software> + <d:state name="Portrait"> + <d:description>The phone in portrait view</d:description> + <d:screen-orientation>portrait</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + </d:device> +</d:devices> diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_hardware.xml b/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_hardware.xml new file mode 100644 index 0000000..a5c3da1 --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_hardware.xml @@ -0,0 +1,80 @@ +<?xml version="1.0"?> +<d:devices + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:d="http://schemas.android.com/sdk/devices/1"> + + <d:device> + <d:name> + Galaxy Nexus + </d:name> + <d:manufacturer> + Samsung + </d:manufacturer> + <d:software> + <d:api-level>14</d:api-level> + <d:live-wallpaper-support>true</d:live-wallpaper-support> + <d:bluetooth-profiles> + HSP + HFP + SPP + A2DP + AVRCP + OPP + PBAP + GAVDP + AVDTP + HID + HDP + PAN + </d:bluetooth-profiles> + <d:gl-version>2.0</d:gl-version> + <!-- + These can be gotten via + javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); + --> + <d:gl-extensions> + GL_EXT_discard_framebuffer + GL_EXT_multi_draw_arrays + GL_EXT_shader_texture_lod + GL_EXT_texture_format_BGRA8888 + GL_IMG_multisampled_render_to_texture + GL_IMG_program_binary + GL_IMG_read_format + GL_IMG_shader_binary + GL_IMG_texture_compression_pvrtc + GL_IMG_texture_format_BGRA8888 + GL_IMG_texture_npot + GL_OES_compressed_ETC1_RGB8_texture + GL_OES_depth_texture + GL_OES_depth24 + GL_OES_EGL_image + GL_OES_EGL_image_external + GL_OES_egl_sync + GL_OES_element_index_uint + GL_OES_fragment_precision_high + GL_OES_get_program_binary + GL_OES_mapbuffer + GL_OES_packed_depth_stencil + GL_OES_required_internalformat + GL_OES_rgb8_rgba8 + GL_OES_standard_derivatives + GL_OES_texture_float + GL_OES_texture_half_float + GL_OES_vertex_array_object + GL_OES_vertex_half_float + </d:gl-extensions> + </d:software> + <d:state name="Portrait" default="true"> + <d:description>The phone in portrait view</d:description> + <d:screen-orientation>portrait</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + <d:state name="Landscape"> + <d:description>The phone in landscape view</d:description> + <d:screen-orientation>landscape</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + </d:device> +</d:devices> diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_software.xml b/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_software.xml new file mode 100644 index 0000000..899110a --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_software.xml @@ -0,0 +1,80 @@ +<?xml version="1.0"?> +<d:devices + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:d="http://schemas.android.com/sdk/devices/1"> + + <d:device> + <d:name> + Galaxy Nexus + </d:name> + <d:manufacturer> + Samsung + </d:manufacturer> + <d:hardware> + <d:screen> + <d:screen-size>normal</d:screen-size> + <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> + <d:pixel-density>xhdpi</d:pixel-density> + <d:screen-ratio>long</d:screen-ratio> + <d:dimensions> + <d:x-dimension>720</d:x-dimension> + <d:y-dimension>1280</d:y-dimension> + </d:dimensions> + <d:xdpi>316</d:xdpi> + <d:ydpi>316</d:ydpi> + <d:touch> + <d:multitouch>jazz-hands</d:multitouch> + <d:mechanism>finger</d:mechanism> + <d:screen-type>capacitive</d:screen-type> + </d:touch> + </d:screen> + <d:networking> + Bluetooth + Wifi + NFC + </d:networking> + <d:sensors> + Accelerometer + Barometer + Gyroscope + Compass + GPS + ProximitySensor + </d:sensors> + <d:mic>true</d:mic> + <d:camera> + <d:location>front</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>false</d:flash> + </d:camera> + <d:camera> + <d:location>back</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>true</d:flash> + </d:camera> + <d:keyboard>nokeys</d:keyboard> + <d:nav>nonav</d:nav> + <d:ram unit="GiB">1</d:ram> + <d:buttons>soft</d:buttons> + <d:internal-storage unit="GiB">16</d:internal-storage> + <d:removable-storage unit="KiB"></d:removable-storage> + <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> + <d:gpu>PowerVR SGX540</d:gpu> + <d:abi> + armeabi + armeabi-v7a + </d:abi> + <!--dock (car, desk, tv, none)--> + <d:dock> + </d:dock> + <!-- plugged in (never, charge, always) --> + <d:plugged-in>charge</d:plugged-in> + </d:hardware> + <d:state name="Portrait" default="true"> + <d:description>The phone in portrait view</d:description> + <d:screen-orientation>portrait</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + </d:device> +</d:devices> diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_states.xml b/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_states.xml new file mode 100644 index 0000000..c4955b4 --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/devices_no_states.xml @@ -0,0 +1,128 @@ +<?xml version="1.0"?> +<d:devices + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:d="http://schemas.android.com/sdk/devices/1"> + + <d:device> + <d:name> + Galaxy Nexus + </d:name> + <d:manufacturer> + Samsung + </d:manufacturer> + <d:hardware> + <d:screen> + <d:screen-size>normal</d:screen-size> + <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> + <d:pixel-density>xhdpi</d:pixel-density> + <d:screen-ratio>long</d:screen-ratio> + <d:dimensions> + <d:x-dimension>720</d:x-dimension> + <d:y-dimension>1280</d:y-dimension> + </d:dimensions> + <d:xdpi>316</d:xdpi> + <d:ydpi>316</d:ydpi> + <d:touch> + <d:multitouch>jazz-hands</d:multitouch> + <d:mechanism>finger</d:mechanism> + <d:screen-type>capacitive</d:screen-type> + </d:touch> + </d:screen> + <d:networking> + Bluetooth + Wifi + NFC + </d:networking> + <d:sensors> + Accelerometer + Barometer + Gyroscope + Compass + GPS + ProximitySensor + </d:sensors> + <d:mic>true</d:mic> + <d:camera> + <d:location>front</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>false</d:flash> + </d:camera> + <d:camera> + <d:location>back</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>true</d:flash> + </d:camera> + <d:keyboard>nokeys</d:keyboard> + <d:nav>nonav</d:nav> + <d:ram unit="GiB">1</d:ram> + <d:buttons>soft</d:buttons> + <d:internal-storage unit="GiB">16</d:internal-storage> + <d:removable-storage unit="KiB"></d:removable-storage> + <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> + <d:gpu>PowerVR SGX540</d:gpu> + <d:abi> + armeabi + armeabi-v7a + </d:abi> + <!--dock (car, desk, tv, none)--> + <d:dock> + </d:dock> + <!-- plugged in (never, charge, always) --> + <d:plugged-in>charge</d:plugged-in> + </d:hardware> + <d:software> + <d:api-level>14</d:api-level> + <d:live-wallpaper-support>true</d:live-wallpaper-support> + <d:bluetooth-profiles> + HSP + HFP + SPP + A2DP + AVRCP + OPP + PBAP + GAVDP + AVDTP + HID + HDP + PAN + </d:bluetooth-profiles> + <d:gl-version>2.0</d:gl-version> + <!-- + These can be gotten via + javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); + --> + <d:gl-extensions> + GL_EXT_discard_framebuffer + GL_EXT_multi_draw_arrays + GL_EXT_shader_texture_lod + GL_EXT_texture_format_BGRA8888 + GL_IMG_multisampled_render_to_texture + GL_IMG_program_binary + GL_IMG_read_format + GL_IMG_shader_binary + GL_IMG_texture_compression_pvrtc + GL_IMG_texture_format_BGRA8888 + GL_IMG_texture_npot + GL_OES_compressed_ETC1_RGB8_texture + GL_OES_depth_texture + GL_OES_depth24 + GL_OES_EGL_image + GL_OES_EGL_image_external + GL_OES_egl_sync + GL_OES_element_index_uint + GL_OES_fragment_precision_high + GL_OES_get_program_binary + GL_OES_mapbuffer + GL_OES_packed_depth_stencil + GL_OES_required_internalformat + GL_OES_rgb8_rgba8 + GL_OES_standard_derivatives + GL_OES_texture_float + GL_OES_texture_half_float + GL_OES_vertex_array_object + GL_OES_vertex_half_float + </d:gl-extensions> + </d:software> + </d:device> +</d:devices> diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/devices_too_many_defaults.xml b/device_validator/dvlib/tests/src/com/android/dvlib/devices_too_many_defaults.xml new file mode 100644 index 0000000..a4e464c --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/devices_too_many_defaults.xml @@ -0,0 +1,140 @@ +<?xml version="1.0"?> +<d:devices + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:d="http://schemas.android.com/sdk/devices/1"> + + <d:device> + <d:name> + Galaxy Nexus + </d:name> + <d:manufacturer> + Samsung + </d:manufacturer> + <d:hardware> + <d:screen> + <d:screen-size>normal</d:screen-size> + <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> + <d:pixel-density>xhdpi</d:pixel-density> + <d:screen-ratio>long</d:screen-ratio> + <d:dimensions> + <d:x-dimension>720</d:x-dimension> + <d:y-dimension>1280</d:y-dimension> + </d:dimensions> + <d:xdpi>316</d:xdpi> + <d:ydpi>316</d:ydpi> + <d:touch> + <d:multitouch>jazz-hands</d:multitouch> + <d:mechanism>finger</d:mechanism> + <d:screen-type>capacitive</d:screen-type> + </d:touch> + </d:screen> + <d:networking> + Bluetooth + Wifi + NFC + </d:networking> + <d:sensors> + Accelerometer + Barometer + Gyroscope + Compass + GPS + ProximitySensor + </d:sensors> + <d:mic>true</d:mic> + <d:camera> + <d:location>front</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>false</d:flash> + </d:camera> + <d:camera> + <d:location>back</d:location> + <d:autofocus>true</d:autofocus> + <d:flash>true</d:flash> + </d:camera> + <d:keyboard>nokeys</d:keyboard> + <d:nav>nonav</d:nav> + <d:ram unit="GiB">1</d:ram> + <d:buttons>soft</d:buttons> + <d:internal-storage unit="GiB">16</d:internal-storage> + <d:removable-storage unit="KiB"></d:removable-storage> + <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> + <d:gpu>PowerVR SGX540</d:gpu> + <d:abi> + armeabi + armeabi-v7a + </d:abi> + <!--dock (car, desk, tv, none)--> + <d:dock> + </d:dock> + <!-- plugged in (never, charge, always) --> + <d:plugged-in>charge</d:plugged-in> + </d:hardware> + <d:software> + <d:api-level>14</d:api-level> + <d:live-wallpaper-support>true</d:live-wallpaper-support> + <d:bluetooth-profiles> + HSP + HFP + SPP + A2DP + AVRCP + OPP + PBAP + GAVDP + AVDTP + HID + HDP + PAN + </d:bluetooth-profiles> + <d:gl-version>2.0</d:gl-version> + <!-- + These can be gotten via + javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); + --> + <d:gl-extensions> + GL_EXT_discard_framebuffer + GL_EXT_multi_draw_arrays + GL_EXT_shader_texture_lod + GL_EXT_texture_format_BGRA8888 + GL_IMG_multisampled_render_to_texture + GL_IMG_program_binary + GL_IMG_read_format + GL_IMG_shader_binary + GL_IMG_texture_compression_pvrtc + GL_IMG_texture_format_BGRA8888 + GL_IMG_texture_npot + GL_OES_compressed_ETC1_RGB8_texture + GL_OES_depth_texture + GL_OES_depth24 + GL_OES_EGL_image + GL_OES_EGL_image_external + GL_OES_egl_sync + GL_OES_element_index_uint + GL_OES_fragment_precision_high + GL_OES_get_program_binary + GL_OES_mapbuffer + GL_OES_packed_depth_stencil + GL_OES_required_internalformat + GL_OES_rgb8_rgba8 + GL_OES_standard_derivatives + GL_OES_texture_float + GL_OES_texture_half_float + GL_OES_vertex_array_object + GL_OES_vertex_half_float + </d:gl-extensions> + </d:software> + <d:state name="Portrait" default="true"> + <d:description>The phone in portrait view</d:description> + <d:screen-orientation>portrait</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + <d:state name="Landscape" default="true"> + <d:description>The phone in landscape view</d:description> + <d:screen-orientation>landscape</d:screen-orientation> + <d:keyboard-state>keyssoft</d:keyboard-state> + <d:nav-state>nonav</d:nav-state> + </d:state> + </d:device> +</d:devices> diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/extras/frame.jpeg b/device_validator/dvlib/tests/src/com/android/dvlib/extras/frame.jpeg new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/extras/frame.jpeg diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/extras/frame.png b/device_validator/dvlib/tests/src/com/android/dvlib/extras/frame.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/extras/frame.png diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixteen.jpeg b/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixteen.jpeg new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixteen.jpeg diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixteen.png b/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixteen.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixteen.png diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixtyfour.jpeg b/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixtyfour.jpeg new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixtyfour.jpeg diff --git a/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixtyfour.png b/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixtyfour.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/device_validator/dvlib/tests/src/com/android/dvlib/extras/sixtyfour.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java index 4cc5f5e..2c9dcbe 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java @@ -194,7 +194,11 @@ public class BaseViewRule extends AbstractViewRule { // Strip off the @id prefix stuff String oldId = node.getStringAttr(ANDROID_URI, ATTR_ID); oldId = stripIdPrefix(ensureValidString(oldId)); - IValidator validator = mRulesEngine.getResourceValidator(); + IValidator validator = mRulesEngine.getResourceValidator("id",//$NON-NLS-1$ + false /*uniqueInProject*/, + true /*uniqueInLayout*/, + false /*exists*/, + oldId); String newId = mRulesEngine.displayInput("New Id:", oldId, validator); if (newId != null && newId.trim().length() > 0) { if (!newId.startsWith(NEW_ID_PREFIX)) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java index b002ff5..f360e23 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java @@ -49,7 +49,7 @@ public final class VersionCheck { /** * The minimum version of the SDK Tools that this version of ADT requires. */ - private final static FullRevision MIN_TOOLS_REV = new FullRevision(17); + private final static FullRevision MIN_TOOLS_REV = new FullRevision(20); /** * Pattern to get the minimum plugin version supported by the SDK. This is read from diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java index 3b9d136..5742888 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java @@ -150,11 +150,22 @@ public final class AaptParser { /** * Error message emitted when aapt skips a file because for example it's name is * invalid, such as a layout file name which starts with _. + * <p> + * This error message is used by AAPT in Tools 19 and earlier. */ private static final Pattern sSkippingPattern = Pattern.compile(" \\(skipping (.+) .+ '(.*)'\\)"); //$NON-NLS-1$ /** + * Error message emitted when aapt skips a file because for example it's name is + * invalid, such as a layout file name which starts with _. + * <p> + * This error message is used by AAPT in Tools 20 and later. + */ + private static final Pattern sNewSkippingPattern = + Pattern.compile(" \\(skipping .+ '(.+)' due to ANDROID_AAPT_IGNORE pattern '.+'\\)"); //$NON-NLS-1$ + + /** * Suffix of error message which points to the first occurrence of a repeated resource * definition. * Example: @@ -409,6 +420,25 @@ public final class AaptParser { continue; } + m = sNewSkippingPattern.matcher(p); + if (m.matches()) { + String location = m.group(1); + + if (location.startsWith(".") //$NON-NLS-1$ + || location.endsWith("~")) { //$NON-NLS-1$ + continue; + } + + // check the values and attempt to mark the file. + if (checkAndMark(location, null, p.trim(), osRoot, project, + AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) { + return true; + } + + // success, go to the next line + continue; + } + m = sSkippingPattern.matcher(p); if (m.matches()) { String location = m.group(2); @@ -424,7 +454,7 @@ public final class AaptParser { // check the values and attempt to mark the file. if (checkAndMark(location, null, p.trim(), osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { + AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) { return true; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java index 396e172..2f3e0b1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java @@ -27,14 +27,12 @@ import com.android.ide.eclipse.adt.internal.lint.EclipseLintRunner; import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener; import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.ResourcesPlugin; @@ -71,11 +69,9 @@ import org.eclipse.ui.forms.editor.IFormPage; import org.eclipse.ui.forms.events.HyperlinkAdapter; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.events.IHyperlinkListener; -import org.eclipse.ui.forms.widgets.FormText; +import org.eclipse.ui.ide.IDEActionFactory; import org.eclipse.ui.ide.IGotoMarker; import org.eclipse.ui.internal.browser.WorkbenchBrowserSupport; -import org.eclipse.ui.part.MultiPageEditorPart; -import org.eclipse.ui.part.WorkbenchPart; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IModelManager; @@ -365,6 +361,21 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh action = mTextEditor.getAction(ActionFactory.REDO.getId()); bars.setGlobalActionHandler(ActionFactory.REDO.getId(), action); + bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), + mTextEditor.getAction(ActionFactory.DELETE.getId())); + bars.setGlobalActionHandler(ActionFactory.CUT.getId(), + mTextEditor.getAction(ActionFactory.CUT.getId())); + bars.setGlobalActionHandler(ActionFactory.COPY.getId(), + mTextEditor.getAction(ActionFactory.COPY.getId())); + bars.setGlobalActionHandler(ActionFactory.PASTE.getId(), + mTextEditor.getAction(ActionFactory.PASTE.getId())); + bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), + mTextEditor.getAction(ActionFactory.SELECT_ALL.getId())); + bars.setGlobalActionHandler(ActionFactory.FIND.getId(), + mTextEditor.getAction(ActionFactory.FIND.getId())); + bars.setGlobalActionHandler(IDEActionFactory.BOOKMARK.getId(), + mTextEditor.getAction(IDEActionFactory.BOOKMARK.getId())); + bars.updateActionBars(); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlEditor.java index 5ee76c2..a7b3660 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlEditor.java @@ -42,7 +42,6 @@ import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.ISourceViewerExtension2; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IShowEditorInput; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinter.java index 22a86af..a5e26bf 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinter.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinter.java @@ -752,6 +752,10 @@ public class XmlPrettyPrinter { return false; } + if (isMarkupElement(element)) { + return false; + } + // See if this element should be separated from the previous element. // This is the case if we are not compressing whitespace (checked above), // or if we are not immediately following a comment (in which case the @@ -876,14 +880,36 @@ public class XmlPrettyPrinter { return false; } + if (isMarkupElement(element)) { + return false; + } + return element.getParentNode().getNodeType() == Node.ELEMENT_NODE && !keepElementAsSingleLine(depth - 1, (Element) element.getParentNode()); } private boolean isMarkupElement(Element element) { - // <u>, <b>, <i>, ... - // http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling - return mStyle == XmlFormatStyle.RESOURCE && element.getTagName().length() == 1; + // The documentation suggests that the allowed tags are <u>, <b> and <i>: + // developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling + // However, the full set of tags accepted by Html.fromHtml is much larger. Therefore, + // instead consider *any* element nested inside a <string> definition to be a markup + // element. See frameworks/base/core/java/android/text/Html.java and look for + // HtmlToSpannedConverter#handleStartTag. + + if (mStyle != XmlFormatStyle.RESOURCE) { + return false; + } + + Node curr = element.getParentNode(); + while (curr != null) { + if (STRING_ELEMENT.equals(curr.getNodeName())) { + return true; + } + + curr = curr.getParentNode(); + } + + return false; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java index 9830f7e..a22b29a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java @@ -15,10 +15,10 @@ */ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; -import static com.android.util.XmlUtils.ANDROID_URI; import static com.android.ide.common.layout.LayoutConstants.ATTR_ID; import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX; import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX; +import static com.android.util.XmlUtils.ANDROID_URI; import static org.eclipse.wst.xml.core.internal.provisional.contenttype.ContentTypeIdForXML.ContentTypeID_XML; import com.android.annotations.NonNull; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java index 287a192..addb8bd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java @@ -1563,6 +1563,9 @@ public class GraphicalEditorPart extends EditorPart return null; } + if (mConfigComposite.isDisposed()) { + return null; + } assert mConfigComposite.getDisplay().getThread() == Thread.currentThread(); // attempt to get a target from the configuration selector. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java index e793983..8337007 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java @@ -16,10 +16,13 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gre; +import static com.android.ide.common.layout.LayoutConstants.ATTR_ID; +import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX; import static com.android.sdklib.SdkConstants.CLASS_FRAGMENT; import static com.android.sdklib.SdkConstants.CLASS_V4_FRAGMENT; import static com.android.tools.lint.detector.api.LintConstants.AUTO_URI; import static com.android.tools.lint.detector.api.LintConstants.URI_PREFIX; +import static com.android.util.XmlUtils.ANDROID_URI; import com.android.annotations.NonNull; import com.android.annotations.Nullable; @@ -30,6 +33,7 @@ import com.android.ide.common.api.IViewMetadata; import com.android.ide.common.api.IViewRule; import com.android.ide.common.api.Margins; import com.android.ide.common.api.Rect; +import com.android.ide.common.layout.BaseViewRule; import com.android.ide.common.resources.ResourceRepository; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.actions.AddCompatibilityJarAction; @@ -45,6 +49,7 @@ import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.adt.internal.resources.CyclicDependencyValidator; +import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.internal.sdk.ProjectState; @@ -91,11 +96,17 @@ import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.dialogs.SelectionDialog; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicReference; /** @@ -208,14 +219,65 @@ class ClientRulesEngine implements IClientRulesEngine { } @Override - public @Nullable IValidator getResourceValidator() { - //return ResourceNameValidator.create(false, mRulesEngine.getEditor().getProject(), - // ResourceType.ID); - return null; + public IValidator getResourceValidator( + @NonNull final String resourceTypeName, final boolean uniqueInProject, + final boolean uniqueInLayout, final boolean exists, final String... allowed) { + return new IValidator() { + private ResourceNameValidator mValidator; + + @Override + public String validate(@NonNull String text) { + if (mValidator == null) { + ResourceType type = ResourceType.getEnum(resourceTypeName); + if (uniqueInLayout) { + assert !uniqueInProject; + assert !exists; + Set<String> existing = new HashSet<String>(); + Document doc = mRulesEngine.getEditor().getModel().getXmlDocument(); + if (doc != null) { + addIds(doc, existing); + } + for (String s : allowed) { + existing.remove(s); + } + mValidator = ResourceNameValidator.create(false, existing, type); + } else { + assert allowed.length == 0; + IProject project = mRulesEngine.getEditor().getProject(); + mValidator = ResourceNameValidator.create(false, project, type); + if (uniqueInProject) { + mValidator.unique(); + } + } + if (exists) { + mValidator.exist(); + } + } + + return mValidator.isValid(text); + } + }; + } + + /** Find declared ids under the given DOM node */ + private static void addIds(Node node, Set<String> ids) { + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) node; + String id = element.getAttributeNS(ANDROID_URI, ATTR_ID); + if (id != null && id.startsWith(NEW_ID_PREFIX)) { + ids.add(BaseViewRule.stripIdPrefix(id)); + } + } + + NodeList children = node.getChildNodes(); + for (int i = 0, n = children.getLength(); i < n; i++) { + Node child = children.item(i); + addIds(child, ids); + } } @Override - public String displayReferenceInput(String currentValue) { + public String displayReferenceInput(@Nullable String currentValue) { GraphicalEditorPart graphicalEditor = mRulesEngine.getEditor(); LayoutEditorDelegate delegate = graphicalEditor.getEditorDelegate(); IProject project = delegate.getEditor().getProject(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java index 657c9ec..f753408 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java @@ -16,7 +16,6 @@ package com.android.ide.eclipse.adt.internal.editors.layout.refactoring; import static com.android.AndroidConstants.FD_RES_LAYOUT; -import static com.android.util.XmlUtils.ANDROID_URI; import static com.android.ide.common.layout.LayoutConstants.ATTR_ID; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX; @@ -30,6 +29,7 @@ import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP; import static com.android.resources.ResourceType.LAYOUT; import static com.android.sdklib.SdkConstants.FD_RES; import static com.android.util.XmlUtils.ANDROID_NS_NAME; +import static com.android.util.XmlUtils.ANDROID_URI; import static com.android.util.XmlUtils.XMLNS; import static com.android.util.XmlUtils.XMLNS_COLON; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintListDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintListDialog.java index 147327d..57a85e8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintListDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintListDialog.java @@ -72,6 +72,7 @@ class LintListDialog extends TitleAreaDialog implements SelectionListener { super(parentShell); mFile = file; mEditor = editor; + setHelpAvailable(false); } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java index 6266ea6..3be2f96 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java @@ -16,6 +16,8 @@ package com.android.ide.eclipse.adt.internal.project; +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.sdklib.SdkConstants; @@ -393,7 +395,7 @@ public final class BaseProjectHelper { * @param filter an optional filter to control which android project are returned. Can be null. * @return an array of IJavaProject, which can be empty if no projects match. */ - public static IJavaProject[] getAndroidProjects(IProjectFilter filter) { + public static @NonNull IJavaProject[] getAndroidProjects(@Nullable IProjectFilter filter) { IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); IJavaModel javaModel = JavaCore.create(workspaceRoot); @@ -408,7 +410,9 @@ public final class BaseProjectHelper { * @param filter an optional filter to control which android project are returned. Can be null. * @return an array of IJavaProject, which can be empty if no projects match. */ - public static IJavaProject[] getAndroidProjects(IJavaModel javaModel, IProjectFilter filter) { + @NonNull + public static IJavaProject[] getAndroidProjects(@NonNull IJavaModel javaModel, + @Nullable IProjectFilter filter) { // get the java projects IJavaProject[] javaProjectList = null; try { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java index 188bbc7..5208ed8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java @@ -42,6 +42,12 @@ public class ResourceNameValidator implements IInputValidator { /** Set of existing names to check for conflicts with */ private Set<String> mExisting; + /** If true, the validated name must be unique */ + private boolean mUnique = true; + + /** If true, the validated name must exist */ + private boolean mExist; + /** * True if the resource name being considered is a "file" based resource (where the * resource name is the actual file name, rather than just a value attribute inside an @@ -65,6 +71,30 @@ public class ResourceNameValidator implements IInputValidator { mIsImageType = isImageType; } + /** + * Makes the resource name validator require that names are unique. + * + * @return this, for construction chaining + */ + public ResourceNameValidator unique() { + mUnique = true; + mExist = false; + + return this; + } + + /** + * Makes the resource name validator require that names already exist + * + * @return this, for construction chaining + */ + public ResourceNameValidator exist() { + mExist = true; + mUnique = false; + + return this; + } + @Override public String isValid(String newText) { // IValidator has the same interface as SWT's IInputValidator @@ -130,8 +160,14 @@ public class ResourceNameValidator implements IInputValidator { return String.format("%1$s is not a valid name (reserved Java keyword)", newText); } - if (mExisting != null && mExisting.contains(newText)) { - return String.format("%1$s already exists", newText); + + if (mExisting != null && (mUnique || mExist)) { + boolean exists = mExisting.contains(newText); + if (mUnique && exists) { + return String.format("%1$s already exists", newText); + } else if (mExist && !exists) { + return String.format("%1$s does not exist", newText); + } } return null; @@ -170,11 +206,12 @@ public class ResourceNameValidator implements IInputValidator { ResourceType type) { boolean isFileType = ResourceHelper.isFileBasedResourceType(type); return new ResourceNameValidator(allowXmlExtension, existing, isFileType, - type == ResourceType.DRAWABLE); + type == ResourceType.DRAWABLE).unique(); } /** - * Creates a new {@link ResourceNameValidator} + * Creates a new {@link ResourceNameValidator}. By default, the name will need to be + * unique in the project. * * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the * resource name diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java index 7b90633..d2747a9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java @@ -21,6 +21,7 @@ import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_R import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF; import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.resources.ResourceItem; import com.android.ide.common.resources.ResourceRepository; @@ -367,15 +368,19 @@ public class ResourceChooser extends AbstractElementListSelectionDialog implemen } else { if (ResourceHelper.isValueBasedResourceType(mResourceType)) { String newName = createNewValue(mResourceType); - selectAddedItem(newName); + if (newName != null) { + selectAddedItem(newName); + } } else { String newName = createNewFile(mResourceType); - selectAddedItem(newName); + if (newName != null) { + selectAddedItem(newName); + } } } } - private void selectAddedItem(String newName) { + private void selectAddedItem(@NonNull String newName) { // Recompute the "current resource" to select the new id ResourceItem[] items = setupResourceList(); @@ -506,6 +511,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog implemen } } + @Nullable private String createNewFile(ResourceType type) { // Show a name/value dialog entering the key name and the value Shell shell = AdtPlugin.getDisplay().getActiveShell(); @@ -538,6 +544,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog implemen } + @Nullable private String createNewValue(ResourceType type) { // Show a name/value dialog entering the key name and the value Shell shell = AdtPlugin.getDisplay().getActiveShell(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/ActivityPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/ActivityPage.java index 9a61b4f..d9f650c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/ActivityPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/ActivityPage.java @@ -15,12 +15,12 @@ */ package com.android.ide.eclipse.adt.internal.wizards.templates; -import static com.android.ide.eclipse.adt.internal.wizards.templates.NewTemplateWizard.ACTIVITY_TEMPLATES; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_PADDING; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_WIDTH; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl; +import com.google.common.io.Files; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; @@ -39,12 +39,16 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.List; -import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; class ActivityPage extends WizardPage implements SelectionListener { private final NewProjectWizardState mValues; private List mList; private Button mCreateToggle; + private java.util.List<File> mTemplates; private boolean mIgnore; private boolean mShown; @@ -64,11 +68,15 @@ class ActivityPage extends WizardPage implements SelectionListener { setDescription("Select whether to create an activity, and if so, what kind of activity."); } - @SuppressWarnings("unused") // SWT constructors have side effects and aren't unused @Override public void createControl(Composite parent) { Composite container = new Composite(parent, SWT.NULL); setControl(container); + } + + @SuppressWarnings("unused") // SWT constructors have side effects and aren't unused + private void onEnter() { + Composite container = (Composite) getControl(); container.setLayout(new GridLayout(3, false)); mCreateToggle = new Button(container, SWT.CHECK); @@ -79,20 +87,29 @@ class ActivityPage extends WizardPage implements SelectionListener { mList = new List(container, SWT.BORDER | SWT.V_SCROLL); mList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); - mList.setItems(ACTIVITY_TEMPLATES); + + + mTemplates = TemplateHandler.getTemplates("activities"); + java.util.List<String> names = new ArrayList<String>(mTemplates.size()); + File current = mValues.activityValues.getTemplateLocation(); int index = -1; - for (int i = 0; i < ACTIVITY_TEMPLATES.length; i++) { - if (ACTIVITY_TEMPLATES[i].equals(mValues.activityValues.getTemplateName())) { + for (int i = 0, n = mTemplates.size(); i < n; i++) { + File template = mTemplates.get(i); + names.add(template.getName()); + if (template.equals(current)) { index = i; - break; } } - if (index == -1) { - mValues.activityValues.setTemplateName(ACTIVITY_TEMPLATES[0]); + String[] items = names.toArray(new String[names.size()]); + mList.setItems(items); + if (index == -1 && !mTemplates.isEmpty()) { + mValues.activityValues.setTemplateLocation(mTemplates.get(0)); index = 0; } - mList.setSelection(index); - mList.addSelectionListener(this); + if (index >= 0) { + mList.setSelection(index); + mList.addSelectionListener(this); + } // Preview mPreview = new ImageControl(container, SWT.NONE, null); @@ -113,26 +130,29 @@ class ActivityPage extends WizardPage implements SelectionListener { mHeading.setFont(font); } - setPreview(mValues.activityValues.getTemplateName()); + updatePreview(); } - private void setPreview(String templateName) { + private void updatePreview() { Image oldImage = mPreviewImage; mPreviewImage = null; String title = ""; String description = ""; - TemplateMetadata template = TemplateHandler.getTemplate(templateName); + TemplateHandler handler = mValues.activityValues.getTemplateHandler(); + TemplateMetadata template = handler.getTemplate(); if (template != null) { String thumb = template.getThumbnailPath(); if (thumb != null && !thumb.isEmpty()) { - String filePath = TemplateHandler.getTemplatePath(templateName) + '/' + thumb; - InputStream input = AdtPlugin.readEmbeddedFileAsStream(filePath); - if (input != null) { + File file = new File(mValues.activityValues.getTemplateLocation(), + thumb.replace('/', File.separatorChar)); + if (file != null) { try { + byte[] bytes = Files.toByteArray(file); + ByteArrayInputStream input = new ByteArrayInputStream(bytes); mPreviewImage = new Image(getControl().getDisplay(), input); input.close(); - } catch (Exception e) { + } catch (IOException e) { AdtPlugin.log(e, null); } } @@ -167,6 +187,10 @@ class ActivityPage extends WizardPage implements SelectionListener { @Override public void setVisible(boolean visible) { + if (visible && !mShown) { + onEnter(); + } + super.setVisible(visible); if (visible) { @@ -233,11 +257,10 @@ class ActivityPage extends WizardPage implements SelectionListener { mList.setEnabled(mValues.createActivity); } else if (source == mList) { int index = mList.getSelectionIndex(); - String[] items = mList.getItems(); - if (index >= 0 && index < items.length) { - String templateName = items[index]; - mValues.activityValues.setTemplateName(templateName); - setPreview(templateName); + if (index >= 0 && index < mTemplates.size()) { + File template = mTemplates.get(index); + mValues.activityValues.setTemplateLocation(template); + updatePreview(); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/FmActivityToLayoutMethod.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/FmActivityToLayoutMethod.java index a6f2a9e..fbd50e9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/FmActivityToLayoutMethod.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/FmActivityToLayoutMethod.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.adt.internal.wizards.templates; import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectPage.ACTIVITY_NAME_SUFFIX; +import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectPage.LAYOUT_NAME_PREFIX; import com.android.ide.eclipse.adt.AdtUtils; @@ -39,6 +40,10 @@ public class FmActivityToLayoutMethod implements TemplateMethodModel { String activityName = args.get(0).toString(); + if (activityName.isEmpty()) { + return new SimpleScalar(""); + } + // Strip off the end portion of the activity name. The user might be typing // the activity name such that only a portion has been entered so far (e.g. // "MainActivi") and we want to chop off that portion too such that we don't @@ -52,7 +57,7 @@ public class FmActivityToLayoutMethod implements TemplateMethodModel { // Convert CamelCase convention used in activity class names to underlined convention // used in layout name: - String name = AdtUtils.camelCaseToUnderlines(activityName); + String name = LAYOUT_NAME_PREFIX + AdtUtils.camelCaseToUnderlines(activityName); return new SimpleScalar(name); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/FmLayoutToActivityMethod.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/FmLayoutToActivityMethod.java index f3dd9cd..6514959 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/FmLayoutToActivityMethod.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/FmLayoutToActivityMethod.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.wizards.templates; import static com.android.ide.eclipse.adt.AdtUtils.extractClassName; import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectPage.ACTIVITY_NAME_SUFFIX; +import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectPage.LAYOUT_NAME_PREFIX; import com.android.ide.eclipse.adt.AdtUtils; @@ -39,6 +40,15 @@ public class FmLayoutToActivityMethod implements TemplateMethodModel { } String name = args.get(0).toString(); + + // Strip off the beginning portion of the layout name. The user might be typing + // the activity name such that only a portion has been entered so far (e.g. + // "MainActivi") and we want to chop off that portion too such that we don't + // offer a layout name partially containing the activity suffix (e.g. "main_activi"). + if (name.startsWith(LAYOUT_NAME_PREFIX)) { + name = name.substring(LAYOUT_NAME_PREFIX.length()); + } + name = AdtUtils.underlinesToCamelCase(name); String className = extractClassName(name); if (className == null) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java index 0c40fab..800366a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java @@ -68,7 +68,9 @@ public class NewProjectPage extends WizardPage implements ModifyListener, SelectionListener, FocusListener { private static final String SAMPLE_PACKAGE_PREFIX = "com.example."; //$NON-NLS-1$ /** Suffix added by default to activity names */ - static final String ACTIVITY_NAME_SUFFIX = "Activity"; //$NON-NLS-1$ + static final String ACTIVITY_NAME_SUFFIX = "Activity"; //$NON-NLS-1$ + /** Prefix added to default layout names */ + static final String LAYOUT_NAME_PREFIX = "activity_"; //$NON-NLS-1$ private static final int INITIAL_MIN_SDK = 8; private final NewProjectWizardState mValues; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java index 60f7a9e..5e359a4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java @@ -41,6 +41,7 @@ import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.jface.wizard.Wizard; import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.INewWizard; import org.eclipse.ui.IWorkbench; @@ -70,6 +71,7 @@ public class NewProjectWizard extends Wizard implements INewWizard { private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$ private IWorkbench mWorkbench; + private UpdateToolsPage mUpdatePage; private NewProjectPage mMainPage; private AppSkeletonPage mAppSkeletonPage; private NewTemplatePage mTemplatePage; @@ -85,6 +87,10 @@ public class NewProjectWizard extends Wizard implements INewWizard { setHelpAvailable(false); setImageDescriptor(); + if (!UpdateToolsPage.isUpToDate()) { + mUpdatePage = new UpdateToolsPage(); + } + mValues = new NewProjectWizardState(); mMainPage = new NewProjectPage(mValues); mAppSkeletonPage = new AppSkeletonPage(mValues); @@ -96,12 +102,24 @@ public class NewProjectWizard extends Wizard implements INewWizard { */ @Override public void addPages() { + if (mUpdatePage != null) { + addPage(mUpdatePage); + } + addPage(mMainPage); addPage(mAppSkeletonPage); addPage(mActivityPage); } @Override + public IWizardPage getStartingPage() { + if (mUpdatePage != null && mUpdatePage.isPageComplete()) { + return mMainPage; + } + return super.getStartingPage(); + } + + @Override public IWizardPage getNextPage(IWizardPage page) { // If you turn off creating an application, only one page if (page == mMainPage) { @@ -197,6 +215,10 @@ public class NewProjectWizard extends Wizard implements INewWizard { @Override public boolean performFinish() { try { + Shell shell = getShell(); + if (shell != null) { + shell.setVisible(false); + } IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); String name = mValues.projectName; final IProject newProject = root.getProject(name); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizardState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizardState.java index 48a781f..1dc81ca 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizardState.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizardState.java @@ -19,19 +19,16 @@ package com.android.ide.eclipse.adt.internal.wizards.templates; import com.android.ide.eclipse.adt.internal.assetstudio.CreateAssetSetWizardState; import com.android.sdklib.IAndroidTarget; -import java.io.File; - /** * Value object which holds the current state of the wizard pages for the * {@link NewProjectWizard} */ public class NewProjectWizardState { - private static final String TEMPLATE_NAME = "NewAndroidApplication"; //$NON-NLS-1$ + private static final String TEMPLATE_NAME = "projects/NewAndroidApplication"; //$NON-NLS-1$ /** Creates a new {@link NewProjectWizardState} */ public NewProjectWizardState() { - File inputPath = new File(TemplateHandler.getTemplatePath(TEMPLATE_NAME)); - template = TemplateHandler.createFromPath(inputPath); + template = TemplateHandler.createFromName(TEMPLATE_NAME); } /** The template handler instantiating the project */ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java index a6fdf48..eece39b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java @@ -20,21 +20,41 @@ import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHan import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_NAME; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_PADDING; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_WIDTH; +import static com.android.sdklib.SdkConstants.CLASS_ACTIVITY; import com.android.annotations.Nullable; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.editors.IconFactory; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl; +import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper; import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.ProjectCombo; +import com.android.ide.eclipse.adt.internal.wizards.templates.Parameter.Type; import com.android.tools.lint.detector.api.LintUtils; import com.google.common.collect.Lists; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeHierarchy; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.ui.IJavaElementSearchConstants; +import org.eclipse.jdt.ui.JavaUI; +import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension; +import org.eclipse.jdt.ui.dialogs.ITypeInfoRequestor; +import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension; +import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.fieldassist.FieldDecoration; import org.eclipse.jface.fieldassist.FieldDecorationRegistry; @@ -54,7 +74,9 @@ import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.SelectionDialog; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -162,11 +184,11 @@ public class NewTemplatePage extends WizardPage if (template != null) { thumb = template.getThumbnailPath(); String title = template.getTitle(); - if (!title.isEmpty()) { + if (title != null && !title.isEmpty()) { setTitle(title); } String description = template.getDescription(); - if (!description.isEmpty()) { + if (description != null && !description.isEmpty()) { setDescription(description); } @@ -210,116 +232,144 @@ public class NewTemplatePage extends WizardPage continue; } - if (type == Parameter.Type.STRING) { - // TODO: Look at the constraints to add validators here - // TODO: If I type.equals("layout") add resource validator for layout - // names - // TODO: If I type.equals("class") make class validator - - // TODO: Handle package and id better later - Label label = new Label(container, SWT.NONE); - label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - label.setText(name); - - Text text = new Text(container, SWT.BORDER); - text.setData(parameter); - parameter.control = text; - text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); - - if (value != null && !value.isEmpty()){ - text.setText(value); - mValues.parameters.put(id, value); - } + switch (type) { + case STRING: { + // TODO: Look at the constraints to add validators here + // TODO: If I type.equals("layout") add resource validator for layout + // names + // TODO: If I type.equals("class") make class validator + + // TODO: Handle package and id better later + Label label = new Label(container, SWT.NONE); + label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, + 1, 1)); + label.setText(name); + + Text text = new Text(container, SWT.BORDER); + text.setData(parameter); + parameter.control = text; + + if (parameter.constraints.contains(Parameter.Constraint.EXISTS)) { + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, + 1, 1)); + + Button button = new Button(container, SWT.FLAT); + button.setData(parameter); + button.setText("..."); + button.addSelectionListener(this); + } else { + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, + 2, 1)); + } - text.addModifyListener(this); - text.addFocusListener(this); + if (value != null && !value.isEmpty()){ + text.setText(value); + mValues.parameters.put(id, value); + } - if (mFirst == null) { - mFirst = text; - } + text.addModifyListener(this); + text.addFocusListener(this); - if (help != null && !help.isEmpty()) { - text.setToolTipText(help); - ControlDecoration decoration = createFieldDecoration(id, text, help); - } - } else if (type == Parameter.Type.BOOLEAN) { - Label label = new Label(container, SWT.NONE); - label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - - Button checkBox = new Button(container, SWT.CHECK); - checkBox.setText(name); - checkBox.setData(parameter); - parameter.control = checkBox; - checkBox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); - - if (value != null && !value.isEmpty()){ - Boolean selected = Boolean.valueOf(value); - checkBox.setSelection(selected); - mValues.parameters.put(id, value); - } - - checkBox.addSelectionListener(this); - checkBox.addFocusListener(this); + if (mFirst == null) { + mFirst = text; + } - if (mFirst == null) { - mFirst = checkBox; + if (help != null && !help.isEmpty()) { + text.setToolTipText(help); + ControlDecoration decoration = createFieldDecoration(id, text, help); + } + break; } + case BOOLEAN: { + Label label = new Label(container, SWT.NONE); + label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, + 1, 1)); + + Button checkBox = new Button(container, SWT.CHECK); + checkBox.setText(name); + checkBox.setData(parameter); + parameter.control = checkBox; + checkBox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, + 2, 1)); + + if (value != null && !value.isEmpty()){ + Boolean selected = Boolean.valueOf(value); + checkBox.setSelection(selected); + mValues.parameters.put(id, value); + } - if (help != null && !help.isEmpty()) { - checkBox.setToolTipText(help); - ControlDecoration decoration = createFieldDecoration(id, checkBox, help); - } + checkBox.addSelectionListener(this); + checkBox.addFocusListener(this); - } else if (type == Parameter.Type.ENUM) { - Label label = new Label(container, SWT.NONE); - label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - label.setText(name); - - Combo combo = new Combo(container, SWT.READ_ONLY); - combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); - - List<Element> options = DomUtilities.getChildren(parameter.element); - assert options.size() > 0; - int selected = 0; - List<String> ids = Lists.newArrayList(); - List<String> labels = Lists.newArrayList(); - for (int i = 0, n = options.size(); i < n; i++) { - Element option = options.get(i); - String optionId = option.getAttribute(ATTR_ID); - assert optionId != null && !optionId.isEmpty() : ATTR_ID; - String isDefault = option.getAttribute(ATTR_DEFAULT); - if (isDefault != null && !isDefault.isEmpty() && - Boolean.valueOf(isDefault)) { - selected = i; + if (mFirst == null) { + mFirst = checkBox; } - NodeList childNodes = option.getChildNodes(); - assert childNodes.getLength() == 1 && - childNodes.item(0).getNodeType() == Node.TEXT_NODE; - String optionLabel = childNodes.item(0).getNodeValue().trim(); - ids.add(optionId); - labels.add(optionLabel); - } - combo.setData(parameter); - parameter.control = combo; - combo.setData(ATTR_ID, ids.toArray(new String[ids.size()])); - assert labels.size() > 0; - combo.setItems(labels.toArray(new String[labels.size()])); - combo.select(selected); - mValues.parameters.put(id, ids.get(selected)); - - combo.addSelectionListener(this); - combo.addFocusListener(this); - - if (mFirst == null) { - mFirst = combo; + + if (help != null && !help.isEmpty()) { + checkBox.setToolTipText(help); + ControlDecoration decoration = createFieldDecoration(id, checkBox, + help); + } + break; } + case ENUM: { + Label label = new Label(container, SWT.NONE); + label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, + 1, 1)); + label.setText(name); + + Combo combo = new Combo(container, SWT.READ_ONLY); + combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, + 2, 1)); + + List<Element> options = DomUtilities.getChildren(parameter.element); + assert options.size() > 0; + int selected = 0; + List<String> ids = Lists.newArrayList(); + List<String> labels = Lists.newArrayList(); + for (int i = 0, n = options.size(); i < n; i++) { + Element option = options.get(i); + String optionId = option.getAttribute(ATTR_ID); + assert optionId != null && !optionId.isEmpty() : ATTR_ID; + String isDefault = option.getAttribute(ATTR_DEFAULT); + if (isDefault != null && !isDefault.isEmpty() && + Boolean.valueOf(isDefault)) { + selected = i; + } + NodeList childNodes = option.getChildNodes(); + assert childNodes.getLength() == 1 && + childNodes.item(0).getNodeType() == Node.TEXT_NODE; + String optionLabel = childNodes.item(0).getNodeValue().trim(); + ids.add(optionId); + labels.add(optionLabel); + } + combo.setData(parameter); + parameter.control = combo; + combo.setData(ATTR_ID, ids.toArray(new String[ids.size()])); + assert labels.size() > 0; + combo.setItems(labels.toArray(new String[labels.size()])); + combo.select(selected); + mValues.parameters.put(id, ids.get(selected)); + + combo.addSelectionListener(this); + combo.addFocusListener(this); + + if (mFirst == null) { + mFirst = combo; + } - if (help != null && !help.isEmpty()) { - combo.setToolTipText(help); - ControlDecoration decoration = createFieldDecoration(id, combo, help); + if (help != null && !help.isEmpty()) { + combo.setToolTipText(help); + ControlDecoration decoration = createFieldDecoration(id, combo, help); + } + break; } - } else { - assert false : type; + case SEPARATOR: + // Already handled above + assert false : type; + break; + default: + assert false : type; } } } @@ -539,19 +589,105 @@ public class NewTemplatePage extends WizardPage String optionId = optionIds[index]; editParameter(combo, optionId); TemplateMetadata template = mValues.getTemplateHandler().getTemplate(); - setPreview(template.getThumbnailPath()); + if (template != null) { + setPreview(template.getThumbnailPath()); + } } } else if (source instanceof Button) { Button button = (Button) source; - editParameter(button, button.getSelection()); + Parameter parameter = (Parameter) button.getData(); + if (parameter.type == Type.BOOLEAN) { + // Checkbox parameter + editParameter(button, button.getSelection()); - TemplateMetadata template = mValues.getTemplateHandler().getTemplate(); - setPreview(template.getThumbnailPath()); + TemplateMetadata template = mValues.getTemplateHandler().getTemplate(); + if (template != null) { + setPreview(template.getThumbnailPath()); + } + } else { + // Choose button for some other parameter, usually a text + String activity = chooseActivity(); + if (activity != null) { + setValue(parameter, activity); + } + } } validatePage(); } + private String chooseActivity() { + try { + // Compute a search scope: We need to merge all the subclasses + // android.app.Fragment and android.support.v4.app.Fragment + IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); + IProject project = mValues.project; + IJavaProject javaProject = BaseProjectHelper.getJavaProject(project); + IType activityType = null; + + if (javaProject != null) { + activityType = javaProject.findType(CLASS_ACTIVITY); + } + if (activityType == null) { + IJavaProject[] projects = BaseProjectHelper.getAndroidProjects(null); + for (IJavaProject p : projects) { + activityType = p.findType(CLASS_ACTIVITY); + if (activityType != null) { + break; + } + } + } + if (activityType != null) { + NullProgressMonitor monitor = new NullProgressMonitor(); + ITypeHierarchy hierarchy = activityType.newTypeHierarchy(monitor); + IType[] classes = hierarchy.getAllSubtypes(activityType); + scope = SearchEngine.createJavaSearchScope(classes, IJavaSearchScope.SOURCES); + } + + Shell parent = AdtPlugin.getDisplay().getActiveShell(); + final SelectionDialog dialog = JavaUI.createTypeDialog( + parent, + new ProgressMonitorDialog(parent), + scope, + IJavaElementSearchConstants.CONSIDER_CLASSES, false, + // Use ? as a default filter to fill dialog with matches + "?", //$NON-NLS-1$ + new TypeSelectionExtension() { + @Override + public ITypeInfoFilterExtension getFilterExtension() { + return new ITypeInfoFilterExtension() { + @Override + public boolean select(ITypeInfoRequestor typeInfoRequestor) { + int modifiers = typeInfoRequestor.getModifiers(); + if (!Flags.isPublic(modifiers) + || Flags.isInterface(modifiers) + || Flags.isEnum(modifiers)) { + return false; + } + return true; + } + }; + } + }); + + dialog.setTitle("Choose Activity Class"); + dialog.setMessage("Select an Activity class (? = any character, * = any string):"); + if (dialog.open() == IDialogConstants.CANCEL_ID) { + return null; + } + + Object[] types = dialog.getResult(); + if (types != null && types.length > 0) { + return ((IType) types[0]).getFullyQualifiedName(); + } + } catch (JavaModelException e) { + AdtPlugin.log(e, null); + } catch (CoreException e) { + AdtPlugin.log(e, null); + } + return null; + } + private void editParameter(Control control, Object value) { Parameter parameter = getParameter(control); if (parameter != null) { @@ -576,25 +712,7 @@ public class NewTemplatePage extends WizardPage } String updated = mEvaluator.evaluate(p.suggest, mParameters); if (updated != null && !updated.equals(p.value)) { - p.value = updated; - mValues.parameters.put(p.id, updated); - - // Update form widgets - boolean prevIgnore = mIgnore; - try { - mIgnore = true; - if (p.control instanceof Text) { - ((Text) p.control).setText(updated); - } else if (p.control instanceof Button) { - // TODO: Handle - } else if (p.control instanceof Combo) { - // TODO: Handle - } else if (p.control != null) { - assert false : p.control; - } - } finally { - mIgnore = prevIgnore; - } + setValue(p, updated); } } catch (Throwable t) { // Pass: Ignore updating if something wrong happens @@ -604,6 +722,28 @@ public class NewTemplatePage extends WizardPage } } + private void setValue(Parameter p, String value) { + p.value = value; + mValues.parameters.put(p.id, value); + + // Update form widgets + boolean prevIgnore = mIgnore; + try { + mIgnore = true; + if (p.control instanceof Text) { + ((Text) p.control).setText(value); + } else if (p.control instanceof Button) { + // TODO: Handle + } else if (p.control instanceof Combo) { + // TODO: Handle + } else if (p.control != null) { + assert false : p.control; + } + } finally { + mIgnore = prevIgnore; + } + } + @Override public void widgetDefaultSelected(SelectionEvent e) { } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java index b7ad998..143db78 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java @@ -34,6 +34,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.jface.wizard.Wizard; import org.eclipse.ui.INewWizard; import org.eclipse.ui.IWorkbench; @@ -50,18 +51,16 @@ import java.util.Set; */ public class NewTemplateWizard extends Wizard implements INewWizard { /** Template name and location under /templates in the plugin */ - static final String BLANK_ACTIVITY = "BlankActivity"; //$NON-NLS-1$ + static final String BLANK_ACTIVITY = "activities/BlankActivity"; //$NON-NLS-1$ /** Template name and location under /templates in the plugin */ - static final String MASTER_DETAIL_FLOW = "MasterDetailFlow"; //$NON-NLS-1$ + static final String MASTER_DETAIL_FLOW = "activities/MasterDetailFlow"; //$NON-NLS-1$ /** Template name and location under /templates in the plugin */ - static final String CUSTOM_VIEW = "CustomView"; //$NON-NLS-1$ - /** Available activity-templates (included in a list in the new project template) */ - static final String[] ACTIVITY_TEMPLATES = - new String[] { BLANK_ACTIVITY, MASTER_DETAIL_FLOW }; - private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$ + static final String CUSTOM_VIEW = "other/CustomView"; //$NON-NLS-1$ + private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$ protected IWorkbench mWorkbench; protected NewTemplatePage mMainPage; + protected UpdateToolsPage mUpdatePage; protected NewTemplateWizardState mValues; private final String mTemplateName; @@ -77,9 +76,15 @@ public class NewTemplateWizard extends Wizard implements INewWizard { setImageDescriptor(); mValues = new NewTemplateWizardState(); - mValues.setTemplateName(mTemplateName); + + File template = TemplateHandler.getTemplateLocation(mTemplateName); + mValues.setTemplateLocation(template); hideBuiltinParameters(); + if (!UpdateToolsPage.isUpToDate()) { + mUpdatePage = new UpdateToolsPage(); + } + List<IProject> projects = AdtUtils.getSelectedProjects(selection); if (projects.size() == 1) { mValues.project = projects.get(0); @@ -103,10 +108,27 @@ public class NewTemplateWizard extends Wizard implements INewWizard { @Override public void addPages() { + if (mUpdatePage != null) { + addPage(mUpdatePage); + } + addPage(mMainPage); } @Override + public IWizardPage getNextPage(IWizardPage page) { + return super.getNextPage(page); + } + + @Override + public IWizardPage getStartingPage() { + if (mUpdatePage != null && mUpdatePage.isPageComplete()) { + return mMainPage; + } + return super.getStartingPage(); + } + + @Override public boolean performFinish() { try { Map<String, Object> parameters = mValues.parameters; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java index dc75a71..1fb73d8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java @@ -33,9 +33,6 @@ import java.util.Set; * {@link NewTemplateWizard} */ public class NewTemplateWizardState { - /** Name of the template being created */ - private String mTemplateName = BLANK_ACTIVITY; - /** Template handler responsible for instantiating templates and reading resources */ private TemplateHandler mTemplateHandler; @@ -55,7 +52,7 @@ public class NewTemplateWizardState { */ public IProject project; - /** Name of the template being created */ + /** Location of the template being created */ private File mTemplateLocation; /** @@ -65,31 +62,14 @@ public class NewTemplateWizardState { } @NonNull - String getTemplateName() { - return mTemplateName; - } - - /** - * Sets the new template name to use - * - * @param templateName the name of the template to use - */ - void setTemplateName(@NonNull String templateName) { - if (!templateName.equals(mTemplateName)) { - mTemplateName = templateName; - mTemplateLocation = null; - mTemplateHandler = null; - } - } - - @NonNull TemplateHandler getTemplateHandler() { if (mTemplateHandler == null) { File inputPath; if (mTemplateLocation != null) { inputPath = mTemplateLocation; } else { - inputPath = new File(TemplateHandler.getTemplatePath(mTemplateName)); + // Default + inputPath = TemplateHandler.getTemplateLocation(BLANK_ACTIVITY); } mTemplateHandler = TemplateHandler.createFromPath(inputPath); } @@ -97,12 +77,18 @@ public class NewTemplateWizardState { return mTemplateHandler; } - // For template development/testing only + /** Sets the current template */ void setTemplateLocation(File file) { if (!file.equals(mTemplateLocation)) { mTemplateLocation = file; - mTemplateName = null; mTemplateHandler = null; } } + + /** Returns the current template */ + File getTemplateLocation() { + return mTemplateLocation; + } + + } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/Parameter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/Parameter.java index 0b8b952..7436a26 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/Parameter.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/Parameter.java @@ -97,6 +97,9 @@ class Parameter { /** The associated value must not be empty */ NONEMPTY, + /** The associated value should represent a fully qualified activity class name */ + ACTIVITY, + /** The associated value should represent an API level */ APILEVEL, @@ -275,10 +278,19 @@ class Parameter { mValidator = ResourceNameValidator.create(false, ResourceFolderType.DRAWABLE); } return mValidator; - } else if (constraints.contains(Constraint.CLASS)) { + } else if (constraints.contains(Constraint.CLASS) + || constraints.contains(Constraint.ACTIVITY)) { mValidator = new IInputValidator() { @Override public String isValid(String newText) { + // Deliberately allow empty strings for parameters that aren't required + if (newText.trim().isEmpty() + && !constraints.contains(Constraint.NONEMPTY)) { + return null; + } + + // TODO: Don't use *activity* validator if it doesn't have to be one + // (e.g. is CLASS, not ACTIVITY) IStatus status = ApplicationInfoPage.validateActivity(newText.trim()); if (status != null && !status.isOK()) { return status.getMessage(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java index 4f107fb..760084d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java @@ -16,8 +16,9 @@ package com.android.ide.eclipse.adt.internal.wizards.templates; import static com.android.ide.eclipse.adt.AdtConstants.DOT_FTL; -import static com.android.ide.eclipse.adt.AdtConstants.DOT_JAR; import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML; +import static com.android.sdklib.SdkConstants.FD_TEMPLATES; +import static com.android.sdklib.SdkConstants.FD_TOOLS; import com.android.annotations.NonNull; import com.android.annotations.Nullable; @@ -27,11 +28,11 @@ import com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatPreferen import com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatStyle; import com.android.ide.eclipse.adt.internal.editors.formatting.XmlPrettyPrinter; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; +import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; import com.android.manifmerger.ManifestMerger; import com.android.resources.ResourceFolderType; import com.android.sdklib.SdkConstants; import com.google.common.base.Charsets; -import com.google.common.io.ByteStreams; import com.google.common.io.Files; import freemarker.cache.TemplateLoader; @@ -54,22 +55,16 @@ import org.xml.sax.helpers.DefaultHandler; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; -import java.net.URI; import java.net.URL; -import java.security.CodeSource; import java.util.ArrayList; import java.util.Arrays; -import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; @@ -98,10 +93,7 @@ class TemplateHandler { * Shared resource directory containing common resources shared among * multiple templates */ - private static final String RESOURCE_ROOT = "res"; //$NON-NLS-1$ - - /** Relative path within the ADT plugin where the templates are found */ - static final String TEMPLATE_PREFIX = "/templates/"; //$NON-NLS-1$ + private static final String RESOURCE_ROOT = "resources"; //$NON-NLS-1$ /** Reserved filename which describes each template */ static final String TEMPLATE_XML = "template.xml"; //$NON-NLS-1$ @@ -132,6 +124,11 @@ class TemplateHandler { static final String ATTR_FROM = "from"; //$NON-NLS-1$ static final String ATTR_CONSTRAINTS = "constraints";//$NON-NLS-1$ + static final String CATEGORY_ACTIVITIES = "activities";//$NON-NLS-1$ + static final String CATEGORY_PROJECTS = "projects"; //$NON-NLS-1$ + static final String CATEGORY_OTHER = "other"; //$NON-NLS-1$ + + /** Default padding to apply in wizards around the thumbnail preview images */ static final int PREVIEW_PADDING = 10; @@ -145,6 +142,7 @@ class TemplateHandler { private final List<String> mOpen = Lists.newArrayList(); /** Path to the directory containing the templates */ + @NonNull private final File mRootPath; /** The template loader which is responsible for finding (and sharing) template files */ @@ -172,6 +170,12 @@ class TemplateHandler { return new TemplateHandler(rootPath); } + /** Creates a new {@link TemplateHandler} for the template name, which should + * be relative to the templates directory */ + static TemplateHandler createFromName(String relative) { + return new TemplateHandler(new File(getTemplateRootFolder(), relative)); + } + private TemplateHandler(File rootPath) { mRootPath = rootPath; mLoader = new MyTemplateLoader(); @@ -244,26 +248,75 @@ class TemplateHandler { @Nullable public static TemplateMetadata getTemplate(String templateName) { - String relative = getTemplatePath(templateName) + '/' +TEMPLATE_XML; - String xml = AdtPlugin.readEmbeddedTextFile(relative); - Document doc = DomUtilities.parseDocument(xml, true); - if (doc != null && doc.getDocumentElement() != null) { - return new TemplateMetadata(doc); + String relative = templateName + '/' + TEMPLATE_XML; + + File templateFile = getTemplateLocation(relative); + if (templateFile != null) { + try { + String xml = Files.toString(templateFile, Charsets.UTF_8); + Document doc = DomUtilities.parseDocument(xml, true); + if (doc != null && doc.getDocumentElement() != null) { + return new TemplateMetadata(doc); + } + } catch (IOException e) { + AdtPlugin.log(e, null); + return null; + } } return null; } @NonNull - public static String getTemplatePath(String templateName) { - return TEMPLATE_PREFIX + templateName; - } - - @NonNull public String getResourcePath(String templateName) { return new File(mRootPath.getPath(), templateName).getPath(); } + @Nullable + public static File getTemplateRootFolder() { + String location = AdtPrefs.getPrefs().getOsSdkFolder(); + if (location != null) { + File folder = new File(location, FD_TOOLS + File.separator + FD_TEMPLATES); + if (folder.isDirectory()) { + return folder; + } + } + + return null; + } + + @Nullable + public static File getTemplateLocation(@NonNull File root, @NonNull String relativePath) { + File templateRoot = getTemplateRootFolder(); + if (templateRoot != null) { + String rootPath = root.getPath(); + File templateFile = new File(templateRoot, + rootPath.replace('/', File.separatorChar) + File.separator + + relativePath.replace('/', File.separatorChar)); + if (templateFile.exists()) { + return templateFile; + } + } + + return null; + + } + + @Nullable + public static File getTemplateLocation(@NonNull String relativePath) { + File templateRoot = getTemplateRootFolder(); + if (templateRoot != null) { + File templateFile = new File(templateRoot, + relativePath.replace('/', File.separatorChar)); + if (templateFile.exists()) { + return templateFile; + } + } + + return null; + + } + /** * Load a text resource for the given relative path within the template * @@ -272,29 +325,23 @@ class TemplateHandler { */ @Nullable public String readTemplateTextResource(@NonNull String relativePath) { - if (mRootPath.getPath().startsWith(TEMPLATE_PREFIX)) { - return AdtPlugin.readEmbeddedTextFile(getResourcePath(relativePath)); - } else { - try { - return Files.toString(new File(mRootPath, relativePath), Charsets.UTF_8); - } catch (IOException e) { - AdtPlugin.log(e, null); - return null; - } + try { + return Files.toString(new File(mRootPath, + relativePath.replace('/', File.separatorChar)), Charsets.UTF_8); + } catch (IOException e) { + AdtPlugin.log(e, null); + return null; } } @Nullable public String readTemplateTextResource(@NonNull File file) { - if (mRootPath.getPath().startsWith(TEMPLATE_PREFIX)) { - return AdtPlugin.readEmbeddedTextFile(file.getPath()); - } else { - try { - return Files.toString(file, Charsets.UTF_8); - } catch (IOException e) { - AdtPlugin.log(e, null); - return null; - } + assert file.isAbsolute(); + try { + return Files.toString(file, Charsets.UTF_8); + } catch (IOException e) { + AdtPlugin.log(e, null); + return null; } } @@ -306,15 +353,11 @@ class TemplateHandler { */ @Nullable public byte[] readTemplateResource(@NonNull String relativePath) { - if (mRootPath.getPath().startsWith(TEMPLATE_PREFIX)) { - return AdtPlugin.readEmbeddedFile(getResourcePath(relativePath)); - } else { - try { - return Files.toByteArray(new File(mRootPath, relativePath)); - } catch (IOException e) { - AdtPlugin.log(e, null); - return null; - } + try { + return Files.toByteArray(new File(mRootPath, relativePath)); + } catch (IOException e) { + AdtPlugin.log(e, null); + return null; } } @@ -326,6 +369,9 @@ class TemplateHandler { if (file.endsWith(DOT_XML)) { // Just read the file xml = readTemplateTextResource(file); + if (xml == null) { + return; + } } else { mLoader.setTemplateFile(new File(mRootPath, file)); Template inputsTemplate = freemarker.getTemplate(file); @@ -366,7 +412,8 @@ class TemplateHandler { execute(freemarker, path, paramMap, outputPath); } } else if (!name.equals("template") && !name.equals("category") - && !name.equals("option")) { + && !name.equals("option") && !name.equals(TAG_THUMBS) && + !name.equals(TAG_THUMB)) { System.err.println("WARNING: Unknown template directive " + name); } } @@ -492,10 +539,12 @@ class TemplateHandler { } } - private File getFullPath(String fromPath) { + @NonNull + private File getFullPath(@NonNull String fromPath) { if (fromPath.startsWith(VALUE_TEMPLATE_DIR)) { - return new File(mRootPath.getParentFile(), RESOURCE_ROOT - + fromPath.substring(VALUE_TEMPLATE_DIR.length())); + return new File(getTemplateRootFolder(), RESOURCE_ROOT + File.separator + + fromPath.substring(VALUE_TEMPLATE_DIR.length() + 1).replace('/', + File.separatorChar)); } return new File(mRootPath, DATA_ROOT + File.separator + fromPath); } @@ -616,37 +665,6 @@ class TemplateHandler { } } - /** - * Writes the given contents into the given file (unless that file already - * contains the given contents), and if the file exists ask user whether - * the file should be overwritten (unless the user has already answered "Yes to All" - * or "Cancel" (no to all). - */ - private void writeBytes(File destination, byte[] contents, boolean confirmOverwrite) - throws IOException { - // First make sure that the files aren't identical, in which case we can do - // nothing (and not involve user) - if (!(destination.exists() && isIdentical(contents, destination))) { - // And if the file does exist (and is now known to be different), - // ask user whether it should be replaced (canOverwrite will also - // return true if the file doesn't exist) - if (confirmOverwrite) { - if (!canOverwrite(destination)) { - return; - } - } else { - if (destination.exists()) { - if (mBackupMergedFiles) { - makeBackup(destination); - } else { - destination.delete(); - } - } - } - Files.write(contents, destination); - } - } - /** Merges the given resource file contents into the given resource file * @param paramMap */ private boolean mergeResourceFile(Document currentManifest, Document fragment, @@ -810,60 +828,11 @@ class TemplateHandler { } /** Copy a bundled resource (part of the plugin .jar file) into the given file system path */ - private final void copyBundledResource(String relativeFrom, File output) throws IOException { + private final void copyBundledResource( + @NonNull String relativeFrom, + @NonNull File output) throws IOException { File from = getFullPath(relativeFrom); - - // Local file copy? (Only used for the template-development wizard) - if (!mRootPath.getPath().startsWith(TEMPLATE_PREFIX)) { - copy(from, output); - return; - } - - String resourcePath = from.getPath(); - CodeSource source = TemplateHandler.class.getProtectionDomain().getCodeSource(); - if (source != null) { - URL location = source.getLocation(); - try { - URI locationUri = location.toURI(); - File locationFile = new File(locationUri); - if (!locationUri.getPath().endsWith(DOT_JAR)) { - // Plain file; e.g. when running out of Eclipse plugin in - // Eclipse; it uses the bin/ folder instead of running out of a jar - File sourceFile = new File(locationFile, resourcePath); - copy(sourceFile, output); - return; - } - - // Copy out of jar file - JarFile jarFile = new JarFile(locationFile); - int chopIndex = resourcePath.length() + 1; - for (final Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements();) { - final JarEntry entry = e.nextElement(); - if (entry.getName().startsWith(resourcePath)) { - final String filename = entry.getName().substring(chopIndex); - assert entry.getName().charAt(resourcePath.length()) == '/'; - final File file = new File(output, filename); - if (!entry.isDirectory()) { - // Copy stream - InputStream in = jarFile.getInputStream(entry); - try { - byte[] data = ByteStreams.toByteArray(in); - writeBytes(output, data, true); - } finally { - in.close(); - } - } else { - // Create directory - if (!file.exists() && !file.mkdirs()) { - throw new IOException("Could not create directory " + file); - } - } - } - } - } catch (Exception e) { - AdtPlugin.log(e, null); - } - } + copy(from, output); } /** Returns true if the given file contains the given bytes */ @@ -939,21 +908,36 @@ class TemplateHandler { @Override public Object findTemplateSource(String name) throws IOException { String path = mPrefix != null ? mPrefix + '/' + name : name; - URL resource = TemplateHandler.class.getResource(path); - - // Support for local files during template development - if (resource == null && mPrefix != null && !mPrefix.startsWith(TEMPLATE_PREFIX)) { - File file = new File(path); - if (file.exists()) { - return file.toURI().toURL(); - } + File file = new File(path); + if (file.exists()) { + return file.toURI().toURL(); } - - return resource; + return null; } @Override public void closeTemplateSource(Object templateSource) throws IOException { } } + + /** Returns all the templates with the given prefix + * + * @param folder the folder prefix + * @return the available templates + */ + @NonNull + static List<File> getTemplates(@NonNull String folder) { + List<File> templates = new ArrayList<File>(); + File root = getTemplateRootFolder(); + if (root != null) { + File[] files = new File(root, folder).listFiles(); + if (files != null) { + for (File file : files) { + templates.add(file); + } + } + } + + return templates; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/UpdateToolsPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/UpdateToolsPage.java new file mode 100644 index 0000000..72713b2 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/UpdateToolsPage.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.ide.eclipse.adt.internal.wizards.templates; + +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +class UpdateToolsPage extends WizardPage implements SelectionListener { + private Button mInstallButton; + UpdateToolsPage() { + super("update"); + setTitle("Update Tools"); + validatePage(); + } + + @Override + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NULL); + setControl(container); + container.setLayout(new GridLayout(1, false)); + + Label label = new Label(container, SWT.WRAP); + GridData layoutData = new GridData(SWT.LEFT, SWT.TOP, true, true, 1, 1); + layoutData.widthHint = NewTemplatePage.WIZARD_PAGE_WIDTH - 50; + label.setLayoutData(layoutData); + label.setText( + "Your tools installation appears to be out of date (or not yet installed).\n" + + "\n" + + "This wizard depends on templates distributed with the Android SDK Tools.\n" + + "\n" + + "Please update the tools first (via Window > Android SDK Manager, or by " + + "using the \"android\" command in a terminal window). Note that on Windows " + + "you may need to restart the IDE, since there are some known problems where " + + "Windows locks the files held open by the running IDE, so the updater is " + + "unable to delete them in order to upgrade them."); + + mInstallButton = new Button(container, SWT.NONE); + mInstallButton.setText("Check Again"); + mInstallButton.addSelectionListener(this); + } + + @Override + public boolean isPageComplete() { + return isUpToDate(); + } + + static boolean isUpToDate() { + return TemplateHandler.getTemplateRootFolder() != null; + } + + private void validatePage() { + boolean ok = isUpToDate(); + setPageComplete(ok); + if (ok) { + setErrorMessage(null); + setMessage(null); + } else { + setErrorMessage("The tools need to be updated via the SDK Manager"); + } + } + + // ---- Implements SelectionListener ---- + + @Override + public void widgetSelected(SelectionEvent e) { + if (e.getSource() == mInstallButton) { + validatePage(); + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/messages.properties b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/messages.properties index 1478f97..b37b809 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/messages.properties +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/messages.properties @@ -1,7 +1,7 @@ Could_Not_Find_Folder=Could not find SDK folder '%1$s'. Could_Not_Find_Folder_In_SDK=Could not find folder '%1$s' inside SDK '%2$s'. Could_Not_Find=Could not find %1$s\! -VersionCheck_Tools_Too_Old=This version of ADT requires Android SDK Tools in revision %1$d or above.\n\nCurrent revision is %2$d.\n\nPlease update your SDK Tools to the latest version. +VersionCheck_Tools_Too_Old=This version of ADT requires Android SDK Tools in revision %1$s or above.\n\nCurrent revision is %2$s.\n\nPlease update your SDK Tools to the latest version. VersionCheck_Plugin_Version_Failed=Failed to get the required ADT version number from the SDK.\n\nThe Android Developer Toolkit may not work properly. VersionCheck_Unable_To_Parse_Version_s=Unable to parse sdk build version: %1$s VersionCheck_Plugin_Too_Old=This Android SDK requires Android Developer Toolkit version %1$d.%2$d.%3$d or above.\n\nCurrent version is %4$s.\n\nPlease update ADT to the latest version. diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java index bf14de3..3514db0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java @@ -81,6 +81,14 @@ public class LogCatView extends SelectionDependentViewPart { mLogCatPanel.selectAll(); } }); + + actionBars.setGlobalActionHandler(ActionFactory.FIND.getId(), + new Action("Find") { + @Override + public void run() { + mLogCatPanel.showFindDialog(); + } + }); } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/.classpath b/eclipse/plugins/com.android.ide.eclipse.gldebugger/.classpath index ac403f0..59d5fe5 100755 --- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/.classpath @@ -6,5 +6,6 @@ <classpathentry kind="lib" path="libs/host-libprotobuf-java-2.3.0-lite.jar"/> <classpathentry kind="lib" path="libs/liblzf.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> + <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java index 7a9f167..fc99764 100644 --- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java +++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java @@ -16,6 +16,8 @@ package com.android.ide.eclipse.gltrace.editors; +import com.android.ddmuilib.FindDialog; +import com.android.ddmuilib.AbstractBufferFindTarget; import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.Function; import com.android.ide.eclipse.gltrace.SwtUtils; import com.android.ide.eclipse.gltrace.TraceFileParserTask; @@ -28,6 +30,7 @@ import com.android.ide.eclipse.gltrace.views.FrameSummaryViewPage; import com.android.ide.eclipse.gltrace.views.detail.DetailsPage; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.viewers.CellLabelProvider; @@ -42,6 +45,9 @@ import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ModifyEvent; @@ -62,10 +68,12 @@ import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IURIEditorInput; import org.eclipse.ui.PartInitException; +import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.part.EditorPart; import java.io.File; @@ -80,6 +88,7 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi public static final String ID = "com.android.ide.eclipse.gltrace.GLFunctionTrace"; //$NON-NLS-1$ private static final String DEFAULT_FILTER_MESSAGE = "Filter list of OpenGL calls. Accepts Java regexes."; + private static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$ /** Width of thumbnail images of the framebuffer. */ private static final int THUMBNAIL_WIDTH = 50; @@ -196,6 +205,31 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi refreshUI(); } }); + + IActionBars actionBars = getEditorSite().getActionBars(); + actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), + new Action("Copy") { + @Override + public void run() { + copySelectionToClipboard(); + } + }); + + actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), + new Action("Select All") { + @Override + public void run() { + selectAll(); + } + }); + + actionBars.setGlobalActionHandler(ActionFactory.FIND.getId(), + new Action("Find") { + @Override + public void run() { + showFindDialog(); + } + }); } private void refreshUI() { @@ -362,7 +396,7 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi GridData gd = new GridData(GridData.FILL_BOTH); c.setLayoutData(gd); - final Tree tree = new Tree(c, SWT.BORDER | SWT.FULL_SELECTION); + final Tree tree = new Tree(c, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI); gd = new GridData(GridData.FILL_BOTH); tree.setLayoutData(gd); tree.setLinesVisible(true); @@ -673,4 +707,79 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi public DetailsPage getDetailsPage() { return new DetailsPage(mTrace); } + + private void copySelectionToClipboard() { + if (mFrameTreeViewer == null || mFrameTreeViewer.getTree().isDisposed()) { + return; + } + + StringBuilder sb = new StringBuilder(); + + for (TreeItem it: mFrameTreeViewer.getTree().getSelection()) { + Object data = it.getData(); + if (data instanceof GLCallNode) { + sb.append(((GLCallNode) data).getCall()); + sb.append(NEWLINE); + } + } + + if (sb.length() > 0) { + Clipboard cb = new Clipboard(Display.getDefault()); + cb.setContents( + new Object[] { sb.toString() }, + new Transfer[] { TextTransfer.getInstance() }); + cb.dispose(); + } + } + + private void selectAll() { + if (mFrameTreeViewer == null || mFrameTreeViewer.getTree().isDisposed()) { + return; + } + + mFrameTreeViewer.getTree().selectAll(); + } + + private class TraceViewerFindTarget extends AbstractBufferFindTarget { + @Override + public int getItemCount() { + return mFrameTreeViewer.getTree().getItemCount(); + } + + @Override + public String getItem(int index) { + Object data = mFrameTreeViewer.getTree().getItem(index).getData(); + if (data instanceof GLCallNode) { + return ((GLCallNode) data).getCall().toString(); + } + return null; + } + + @Override + public void selectAndReveal(int index) { + Tree t = mFrameTreeViewer.getTree(); + t.deselectAll(); + t.select(t.getItem(index)); + t.showSelection(); + } + + @Override + public int getStartingIndex() { + return 0; + } + }; + + private FindDialog mFindDialog; + private TraceViewerFindTarget mFindTarget = new TraceViewerFindTarget(); + + private void showFindDialog() { + if (mFindDialog != null) { + // the dialog is already displayed + return; + } + + mFindDialog = new FindDialog(Display.getDefault().getActiveShell(), mFindTarget); + mFindDialog.open(); // blocks until find dialog is closed + mFindDialog = null; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.monitor/src/com/android/ide/eclipse/monitor/MonitorActionBarAdvisor.java b/eclipse/plugins/com.android.ide.eclipse.monitor/src/com/android/ide/eclipse/monitor/MonitorActionBarAdvisor.java index 4cf7a94..e31e45e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.monitor/src/com/android/ide/eclipse/monitor/MonitorActionBarAdvisor.java +++ b/eclipse/plugins/com.android.ide.eclipse.monitor/src/com/android/ide/eclipse/monitor/MonitorActionBarAdvisor.java @@ -25,11 +25,14 @@ import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; import org.eclipse.ui.application.ActionBarAdvisor; import org.eclipse.ui.application.IActionBarConfigurer; +import org.eclipse.ui.internal.IWorkbenchGraphicConstants; +import org.eclipse.ui.internal.WorkbenchImages; public class MonitorActionBarAdvisor extends ActionBarAdvisor { private IWorkbenchAction mQuitAction; private IWorkbenchAction mCopyAction; private IWorkbenchAction mSelectAllAction; + private IWorkbenchAction mFindAction; private IWorkbenchAction mOpenPerspectiveAction; private IWorkbenchAction mResetPerspectiveAction; private IWorkbenchAction mPreferencesAction; @@ -50,6 +53,10 @@ public class MonitorActionBarAdvisor extends ActionBarAdvisor { mSelectAllAction = ActionFactory.SELECT_ALL.create(window); register(mSelectAllAction); + mFindAction = ActionFactory.FIND.create(window); + mFindAction.setText("Find..."); // replace the default "Find and Replace..." + register(mFindAction); + mOpenPerspectiveAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG.create(window); register(mOpenPerspectiveAction); @@ -83,6 +90,7 @@ public class MonitorActionBarAdvisor extends ActionBarAdvisor { // contents of Edit menu editMenu.add(mCopyAction); editMenu.add(mSelectAllAction); + editMenu.add(mFindAction); // contents of Window menu windowMenu.add(mOpenPerspectiveAction); diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinterTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinterTest.java index 736931b..731621c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinterTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinterTest.java @@ -236,7 +236,7 @@ public class XmlPrettyPrinterTest extends TestCase { "]>\n" + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + " android:orientation=\"vertical\" >\n" + - " <![CDATA[\n" + + "<![CDATA[\n" + "This is character data!\n" + "<!-- This is not a comment! -->\n" + "and <this is not an element>\n" + @@ -850,8 +850,9 @@ public class XmlPrettyPrinterTest extends TestCase { "<resources>\n" + "\n" + " <string name=\"welcome\">Welcome to <b>Android</b>!</string>\n" + - " <string name=\"glob_settings_top_text\"><b>To install a 24 Clock Widget, please <i>long press</i>\n" + - " in Home Screen.</b> Configure the Global Settings here.</string>\n" + + " <string name=\"glob_settings_top_text\"><b>To install a 24 Clock Widget, " + + "please <i>long press</i> in Home Screen.</b> Configure the Global Settings " + + "here.</string>\n" + "\n" + "</resources>"); } @@ -915,4 +916,25 @@ public class XmlPrettyPrinterTest extends TestCase { "\n" + "</resources>"); } + + public void testComplexString() throws Exception { + checkFormat( + "res/values/strings.xml", + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<resources>\n" + + "<string name=\"progress_completed_export_all\">The database has " + + "<b>successfully</b> been exported into: <br /><br /><font size=\"14\">" + + "\\\"<i>%s</i>\\\"</font></string>" + + "</resources>", + + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<resources>\n" + + "\n" + + " <string name=\"progress_completed_export_all\">The database has " + + "<b>successfully</b> been exported into: <br /><br /><font size=\"14\">" + + "\\\"<i>%s</i>\\\"</font></string>\n" + + "\n" + + "</resources>"); + } + } diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java index 312df7d..826f36c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java @@ -16,8 +16,8 @@ package com.android.ide.common.layout; -import static com.android.util.XmlUtils.ANDROID_URI; import static com.android.ide.common.layout.LayoutConstants.ATTR_ID; +import static com.android.util.XmlUtils.ANDROID_URI; import com.android.annotations.NonNull; import com.android.annotations.Nullable; @@ -265,7 +265,8 @@ public class LayoutTestBase extends TestCase { } @Override - public @NonNull IValidator getResourceValidator() { + public IValidator getResourceValidator(String resourceTypeName, boolean uniqueInProject, + boolean uniqueInLayout, boolean exists, String... allowed) { fail("Not supported in tests yet"); return null; } diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java index 97408c3..af0ba2b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java @@ -20,6 +20,8 @@ import com.android.resources.ResourceFolderType; import com.android.resources.ResourceType; import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import junit.framework.TestCase; @@ -61,4 +63,28 @@ public class ResourceNameValidatorTest extends TestCase { assertTrue(ResourceNameValidator.create(true, ResourceFolderType.DRAWABLE) .isValid("_foo") != null); } + + public void testUniqueOrExists() throws Exception { + Set<String> existing = new HashSet<String>(); + existing.add("foo1"); + existing.add("foo2"); + existing.add("foo3"); + + ResourceNameValidator validator = ResourceNameValidator.create(true, existing, + ResourceType.ID); + validator.unique(); + + assertNull(validator.isValid("foo")); // null: ok (no error message) + assertNull(validator.isValid("foo4")); + assertNotNull(validator.isValid("foo1")); + assertNotNull(validator.isValid("foo2")); + assertNotNull(validator.isValid("foo3")); + + validator.exist(); + assertNotNull(validator.isValid("foo")); + assertNotNull(validator.isValid("foo4")); + assertNull(validator.isValid("foo1")); + assertNull(validator.isValid("foo2")); + assertNull(validator.isValid("foo3")); + } } diff --git a/emulator/opengl/host/tools/emugen/ApiGen.cpp b/emulator/opengl/host/tools/emugen/ApiGen.cpp index bf2d244..6964862 100644 --- a/emulator/opengl/host/tools/emugen/ApiGen.cpp +++ b/emulator/opengl/host/tools/emugen/ApiGen.cpp @@ -418,8 +418,9 @@ static void writeVarEncodingExpression(Var& var, FILE* fp) } else { // encode a non pointer variable if (!var.isVoid()) { - fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n", - var.type()->name().c_str(), varname, + fprintf(fp, "\t\tmemcpy(ptr, &%s, %u); ptr += %u;\n", + varname, + (uint) var.type()->bytes(), (uint) var.type()->bytes()); } } @@ -570,8 +571,8 @@ int ApiGen::genEncoderImpl(const std::string &filename) // encode packet header if needed. if (nvars == 0) { - fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n", e->name().c_str()); - fprintf(fp, "\t*(unsigned int *)(ptr) = (unsigned int) packetSize; ptr += 4;\n"); + fprintf(fp, "\tint tmp = OP_%s;memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str()); + fprintf(fp, "\tmemcpy(ptr, &packetSize, 4); ptr += 4;\n\n"); } if (maxvars == 0) @@ -611,8 +612,8 @@ int ApiGen::genEncoderImpl(const std::string &filename) fprintf(fp, "\t unsigned char *ptr = stream->alloc(packetSize);\n\n"); // encode into the stream; - fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n", e->name().c_str()); - fprintf(fp, "\t*(unsigned int *)(ptr) = (unsigned int) packetSize; ptr += 4;\n\n"); + fprintf(fp, "\tint tmp = OP_%s; memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str()); + fprintf(fp, "\tmemcpy(ptr, &packetSize, 4); ptr += 4;\n\n"); // out variables for (size_t j = 0; j < nvars; j++) { diff --git a/emulator/opengl/system/egl/Android.mk b/emulator/opengl/system/egl/Android.mk index 6778abe..b9c9749 100644 --- a/emulator/opengl/system/egl/Android.mk +++ b/emulator/opengl/system/egl/Android.mk @@ -37,6 +37,6 @@ LOCAL_MODULE_TAGS := debug LOCAL_MODULE_CLASS := ETC include $(BUILD_PREBUILT) -endif # TARGET_PRODUCT in 'full sdk full_x86 sdk_x86 google_sdk google_sdk_x86) +endif # TARGET_PRODUCT in 'full sdk full_x86 full_mips sdk_x86 sdk_mips google_sdk google_sdk_x86 google_sdk_mips) endif # BUILD_EMULATOR_OPENGL_DRIVER != false diff --git a/emulator/opengl/tests/gles_android_wrapper/Android.mk b/emulator/opengl/tests/gles_android_wrapper/Android.mk index f7c8fed..f5254b7 100644 --- a/emulator/opengl/tests/gles_android_wrapper/Android.mk +++ b/emulator/opengl/tests/gles_android_wrapper/Android.mk @@ -48,7 +48,7 @@ $(call emugl-end-module) # Other builds are device-specific and will provide their own # version of this file to point to the appropriate HW EGL libraries. # -ifneq (,$(filter full full_x86 sdk sdk_x86,$(TARGET_PRODUCT))) +ifneq (,$(filter full full_x86 full_mips sdk sdk_x86 sdk_mips,$(TARGET_PRODUCT))) ifeq (,$(BUILD_EMULATOR_OPENGL_DRIVER)) include $(CLEAR_VARS) @@ -61,7 +61,7 @@ LOCAL_MODULE_CLASS := ETC include $(BUILD_PREBUILT) endif # building 'real' driver BUILD_EMULATOR_OPENGL_DRIVER -endif # TARGET_PRODUCT in 'full sdk full_x86 sdk_x86' +endif # TARGET_PRODUCT in 'full sdk full_x86 sdk_x86 full_mips sdk_mips' #### gles_emul.cfg #### include $(CLEAR_VARS) diff --git a/emulator/opengl/tests/translator_tests/GLES_CM/Android.mk b/emulator/opengl/tests/translator_tests/GLES_CM/Android.mk index d47d340..bce56e3 100644 --- a/emulator/opengl/tests/translator_tests/GLES_CM/Android.mk +++ b/emulator/opengl/tests/translator_tests/GLES_CM/Android.mk @@ -9,12 +9,9 @@ LOCAL_SDL_CFLAGS := $(shell $(LOCAL_SDL_CONFIG) --cflags) LOCAL_SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(LOCAL_SDL_CONFIG) --static-libs)) ifeq ($(HOST_OS),darwin) - DARWIN_VERSION := $(strip $(shell sw_vers -productVersion)) - ifneq ($(filter 10.7 10.7.% 10.8 10.8.%,$(DARWIN_VERSION)),) - # OS X 10.7+ needs to be forced to link dylib to avoid problems - # with the dynamic function lookups in SDL 1.2 - LOCAL_SDL_LDLIBS += /usr/lib/dylib1.o - endif + # OS X 10.6+ needs to be forced to link dylib to avoid problems + # with the dynamic function lookups in SDL 1.2 + LOCAL_SDL_LDLIBS += /usr/lib/dylib1.o endif LOCAL_SRC_FILES:= \ diff --git a/emulator/opengl/tests/translator_tests/GLES_V2/Android.mk b/emulator/opengl/tests/translator_tests/GLES_V2/Android.mk index 2371da7..504530f 100644 --- a/emulator/opengl/tests/translator_tests/GLES_V2/Android.mk +++ b/emulator/opengl/tests/translator_tests/GLES_V2/Android.mk @@ -17,13 +17,10 @@ LOCAL_LDLIBS += $(LOCAL_SDL_LDLIBS) LOCAL_STATIC_LIBRARIES += libSDL libSDLmain ifeq ($(HOST_OS),darwin) -DARWIN_VERSION := $(strip $(shell sw_vers -productVersion)) -ifneq ($(filter 10.7 10.7.% 10.8 10.8.%,$(DARWIN_VERSION)),) - # OS X 10.7+ needs to be forced to link dylib to avoid problems + # OS X 10.6+ needs to be forced to link dylib to avoid problems # with the dynamic function lookups in SDL 1.2 LOCAL_LDLIBS += /usr/lib/dylib1.o -endif -$(call emugl-import,libMac_view) + $(call emugl-import,libMac_view) endif $(call emugl-end-module) diff --git a/rule_api/src/com/android/ide/common/api/IClientRulesEngine.java b/rule_api/src/com/android/ide/common/api/IClientRulesEngine.java index b9ea6cb..329f38c 100644 --- a/rule_api/src/com/android/ide/common/api/IClientRulesEngine.java +++ b/rule_api/src/com/android/ide/common/api/IClientRulesEngine.java @@ -106,11 +106,25 @@ public interface IClientRulesEngine { /** * Returns a resource name validator for the current project * - * @return an {@link IValidator} for validating a new resource name in the current - * project + * @param resourceTypeName resource type, such as "id", "string", and so on + * @param uniqueInProject if true, the resource name must be unique in the + * project (not already be defined anywhere else) + * @param uniqueInLayout if true, the resource name must be unique at least + * within the current layout. This only applies to {@code @id} + * resources since only those resources can be defined in-place + * within a layout + * @param exists if true, the resource name must already exist + * @param allowed allowed names (optional). This can for example be used to + * request a unique-in-layout validator, but to remove the + * current value of the node being edited from consideration such + * that it allows you to leave the value the same + * @return an {@link IValidator} for validating a new resource name in the + * current project */ @Nullable - IValidator getResourceValidator(); + IValidator getResourceValidator(@NonNull String resourceTypeName, + boolean uniqueInProject, boolean uniqueInLayout, boolean exists, + String... allowed); /** * Displays an input dialog where the user can enter an Android reference value @@ -119,7 +133,7 @@ public interface IClientRulesEngine { * @return the reference selected by the user, or null */ @Nullable - String displayReferenceInput(String currentValue); + String displayReferenceInput(@Nullable String currentValue); /** * Displays an input dialog where the user can enter an Android resource name of the diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/globals.xml.ftl b/templates/activities/BlankActivity/globals.xml.ftl index 3a26abd..3a26abd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/globals.xml.ftl +++ b/templates/activities/BlankActivity/globals.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/recipe.xml.ftl b/templates/activities/BlankActivity/recipe.xml.ftl index 2ce72db..2ce72db 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/recipe.xml.ftl +++ b/templates/activities/BlankActivity/recipe.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/AndroidManifest.xml.ftl b/templates/activities/BlankActivity/root/AndroidManifest.xml.ftl index ffcce79..ffcce79 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/AndroidManifest.xml.ftl +++ b/templates/activities/BlankActivity/root/AndroidManifest.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/drawable-hdpi/ic_action_search.png b/templates/activities/BlankActivity/root/res/drawable-hdpi/ic_action_search.png Binary files differindex 67de12d..67de12d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/drawable-hdpi/ic_action_search.png +++ b/templates/activities/BlankActivity/root/res/drawable-hdpi/ic_action_search.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/drawable-mdpi/ic_action_search.png b/templates/activities/BlankActivity/root/res/drawable-mdpi/ic_action_search.png Binary files differindex 134d549..134d549 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/drawable-mdpi/ic_action_search.png +++ b/templates/activities/BlankActivity/root/res/drawable-mdpi/ic_action_search.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/drawable-xhdpi/ic_action_search.png b/templates/activities/BlankActivity/root/res/drawable-xhdpi/ic_action_search.png Binary files differindex d699c6b..d699c6b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/drawable-xhdpi/ic_action_search.png +++ b/templates/activities/BlankActivity/root/res/drawable-xhdpi/ic_action_search.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/layout/activity_fragment_container.xml b/templates/activities/BlankActivity/root/res/layout/activity_fragment_container.xml index 3128b5f..3128b5f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/layout/activity_fragment_container.xml +++ b/templates/activities/BlankActivity/root/res/layout/activity_fragment_container.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/layout/activity_pager.xml.ftl b/templates/activities/BlankActivity/root/res/layout/activity_pager.xml.ftl index c8f1604..c8f1604 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/layout/activity_pager.xml.ftl +++ b/templates/activities/BlankActivity/root/res/layout/activity_pager.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/layout/activity_simple.xml b/templates/activities/BlankActivity/root/res/layout/activity_simple.xml index aa34ee3..aa34ee3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/layout/activity_simple.xml +++ b/templates/activities/BlankActivity/root/res/layout/activity_simple.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/menu/main.xml b/templates/activities/BlankActivity/root/res/menu/main.xml index cfc10fd..cfc10fd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/menu/main.xml +++ b/templates/activities/BlankActivity/root/res/menu/main.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/values-large/dimens.xml b/templates/activities/BlankActivity/root/res/values-large/dimens.xml index d8cd7c2..d8cd7c2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/values-large/dimens.xml +++ b/templates/activities/BlankActivity/root/res/values-large/dimens.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/values/dimens.xml b/templates/activities/BlankActivity/root/res/values/dimens.xml index d95a70f..d95a70f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/values/dimens.xml +++ b/templates/activities/BlankActivity/root/res/values/dimens.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/values/strings.xml.ftl b/templates/activities/BlankActivity/root/res/values/strings.xml.ftl index 753649d..753649d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/res/values/strings.xml.ftl +++ b/templates/activities/BlankActivity/root/res/values/strings.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/src/app_package/DropdownActivity.java.ftl b/templates/activities/BlankActivity/root/src/app_package/DropdownActivity.java.ftl index 98c1a8f..98c1a8f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/src/app_package/DropdownActivity.java.ftl +++ b/templates/activities/BlankActivity/root/src/app_package/DropdownActivity.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/src/app_package/SimpleActivity.java.ftl b/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl index 1ebc0fa..1ebc0fa 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/src/app_package/SimpleActivity.java.ftl +++ b/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/src/app_package/TabsActivity.java.ftl b/templates/activities/BlankActivity/root/src/app_package/TabsActivity.java.ftl index ab11a7f..ab11a7f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/src/app_package/TabsActivity.java.ftl +++ b/templates/activities/BlankActivity/root/src/app_package/TabsActivity.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/src/app_package/TabsAndPagerActivity.java.ftl b/templates/activities/BlankActivity/root/src/app_package/TabsAndPagerActivity.java.ftl index eb47519..eb47519 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/root/src/app_package/TabsAndPagerActivity.java.ftl +++ b/templates/activities/BlankActivity/root/src/app_package/TabsAndPagerActivity.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template.xml b/templates/activities/BlankActivity/template.xml index 302e2cc..10e6bc8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template.xml +++ b/templates/activities/BlankActivity/template.xml @@ -20,7 +20,7 @@ type="string" constraints="layout|unique" suggest="${activityToLayout(activityClass)}" - default="main" + default="activity_main" help="The name of the layout to create for the activity" /> <parameter diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity.png b/templates/activities/BlankActivity/template_blank_activity.png Binary files differindex 729dd1c..729dd1c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity.png +++ b/templates/activities/BlankActivity/template_blank_activity.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity_dropdown.png b/templates/activities/BlankActivity/template_blank_activity_dropdown.png Binary files differindex 09fa2cf..09fa2cf 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity_dropdown.png +++ b/templates/activities/BlankActivity/template_blank_activity_dropdown.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity_pager.png b/templates/activities/BlankActivity/template_blank_activity_pager.png Binary files differindex 7cd8e0e..7cd8e0e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity_pager.png +++ b/templates/activities/BlankActivity/template_blank_activity_pager.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity_tabs.png b/templates/activities/BlankActivity/template_blank_activity_tabs.png Binary files differindex 86a09d6..86a09d6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity_tabs.png +++ b/templates/activities/BlankActivity/template_blank_activity_tabs.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity_tabs_pager.png b/templates/activities/BlankActivity/template_blank_activity_tabs_pager.png Binary files differindex 0697a56..0697a56 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/BlankActivity/template_blank_activity_tabs_pager.png +++ b/templates/activities/BlankActivity/template_blank_activity_tabs_pager.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/globals.xml.ftl b/templates/activities/MasterDetailFlow/globals.xml.ftl index 519c081..519c081 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/globals.xml.ftl +++ b/templates/activities/MasterDetailFlow/globals.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/recipe.xml.ftl b/templates/activities/MasterDetailFlow/recipe.xml.ftl index a07635e..a07635e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/recipe.xml.ftl +++ b/templates/activities/MasterDetailFlow/recipe.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/AndroidManifest.xml.ftl b/templates/activities/MasterDetailFlow/root/AndroidManifest.xml.ftl index 1b9aa76..1b9aa76 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/AndroidManifest.xml.ftl +++ b/templates/activities/MasterDetailFlow/root/AndroidManifest.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/layout/activity_content_detail.xml.ftl b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_detail.xml.ftl index 1d6d5dc..1d6d5dc 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/layout/activity_content_detail.xml.ftl +++ b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_detail.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/layout/activity_content_list.xml.ftl b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_list.xml.ftl index 788e763..788e763 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/layout/activity_content_list.xml.ftl +++ b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_list.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/layout/activity_content_twopane.xml.ftl b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_twopane.xml.ftl index c7a2c75..c7a2c75 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/layout/activity_content_twopane.xml.ftl +++ b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_twopane.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/layout/fragment_content_detail.xml.ftl b/templates/activities/MasterDetailFlow/root/res/layout/fragment_content_detail.xml.ftl index 9b7ca72..9b7ca72 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/layout/fragment_content_detail.xml.ftl +++ b/templates/activities/MasterDetailFlow/root/res/layout/fragment_content_detail.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/values-large/refs.xml.ftl b/templates/activities/MasterDetailFlow/root/res/values-large/refs.xml.ftl index f3edd90..f3edd90 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/values-large/refs.xml.ftl +++ b/templates/activities/MasterDetailFlow/root/res/values-large/refs.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/values-sw600dp/refs.xml.ftl b/templates/activities/MasterDetailFlow/root/res/values-sw600dp/refs.xml.ftl index f3edd90..f3edd90 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/values-sw600dp/refs.xml.ftl +++ b/templates/activities/MasterDetailFlow/root/res/values-sw600dp/refs.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/values/strings.xml.ftl b/templates/activities/MasterDetailFlow/root/res/values/strings.xml.ftl index 9b92c7d..9b92c7d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/res/values/strings.xml.ftl +++ b/templates/activities/MasterDetailFlow/root/res/values/strings.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl index a7deaf6..a7deaf6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl +++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl index a0acb1c..a0acb1c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl +++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl index 4bc5216..4bc5216 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl +++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl index 6b4b9a0..6b4b9a0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl +++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/dummy/DummyContent.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/dummy/DummyContent.java.ftl index 2b05416..2b05416 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/root/src/app_package/dummy/DummyContent.java.ftl +++ b/templates/activities/MasterDetailFlow/root/src/app_package/dummy/DummyContent.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/template.xml b/templates/activities/MasterDetailFlow/template.xml index 0eed682..0eed682 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/template.xml +++ b/templates/activities/MasterDetailFlow/template.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/template_master_detail.png b/templates/activities/MasterDetailFlow/template_master_detail.png Binary files differindex f9d3f23..f9d3f23 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/MasterDetailFlow/template_master_detail.png +++ b/templates/activities/MasterDetailFlow/template_master_detail.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/globals.xml.ftl b/templates/other/CustomView/globals.xml.ftl index d2eeb40..d2eeb40 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/globals.xml.ftl +++ b/templates/other/CustomView/globals.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/recipe.xml.ftl b/templates/other/CustomView/recipe.xml.ftl index d152df0..d152df0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/recipe.xml.ftl +++ b/templates/other/CustomView/recipe.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/root/res/layout/sample.xml.ftl b/templates/other/CustomView/root/res/layout/sample.xml.ftl index bdd8c8b..bdd8c8b 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/root/res/layout/sample.xml.ftl +++ b/templates/other/CustomView/root/res/layout/sample.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/root/res/values/attrs.xml.ftl b/templates/other/CustomView/root/res/values/attrs.xml.ftl index 89059d2..89059d2 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/root/res/values/attrs.xml.ftl +++ b/templates/other/CustomView/root/res/values/attrs.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/root/src/app_package/CustomView.java.ftl b/templates/other/CustomView/root/src/app_package/CustomView.java.ftl index e1c7e13..e1c7e13 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/root/src/app_package/CustomView.java.ftl +++ b/templates/other/CustomView/root/src/app_package/CustomView.java.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/template.xml b/templates/other/CustomView/template.xml index 9511566..9511566 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/CustomView/template.xml +++ b/templates/other/CustomView/template.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/globals.xml.ftl b/templates/projects/NewAndroidApplication/globals.xml.ftl index bfc27eb..bfc27eb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/globals.xml.ftl +++ b/templates/projects/NewAndroidApplication/globals.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/recipe.xml.ftl b/templates/projects/NewAndroidApplication/recipe.xml.ftl index b343a10..b343a10 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/recipe.xml.ftl +++ b/templates/projects/NewAndroidApplication/recipe.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/AndroidManifest.xml.ftl b/templates/projects/NewAndroidApplication/root/AndroidManifest.xml.ftl index c97c601..c97c601 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/AndroidManifest.xml.ftl +++ b/templates/projects/NewAndroidApplication/root/AndroidManifest.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-hdpi/ic_action_search.png b/templates/projects/NewAndroidApplication/root/res/drawable-hdpi/ic_action_search.png Binary files differindex 67de12d..67de12d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-hdpi/ic_action_search.png +++ b/templates/projects/NewAndroidApplication/root/res/drawable-hdpi/ic_action_search.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-hdpi/ic_launcher.png b/templates/projects/NewAndroidApplication/root/res/drawable-hdpi/ic_launcher.png Binary files differindex fba1ff0..fba1ff0 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-hdpi/ic_launcher.png +++ b/templates/projects/NewAndroidApplication/root/res/drawable-hdpi/ic_launcher.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-mdpi/ic_action_search.png b/templates/projects/NewAndroidApplication/root/res/drawable-mdpi/ic_action_search.png Binary files differindex 134d549..134d549 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-mdpi/ic_action_search.png +++ b/templates/projects/NewAndroidApplication/root/res/drawable-mdpi/ic_action_search.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-mdpi/ic_launcher.png b/templates/projects/NewAndroidApplication/root/res/drawable-mdpi/ic_launcher.png Binary files differindex 72a445d..72a445d 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-mdpi/ic_launcher.png +++ b/templates/projects/NewAndroidApplication/root/res/drawable-mdpi/ic_launcher.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-xhdpi/ic_action_search.png b/templates/projects/NewAndroidApplication/root/res/drawable-xhdpi/ic_action_search.png Binary files differindex d699c6b..d699c6b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-xhdpi/ic_action_search.png +++ b/templates/projects/NewAndroidApplication/root/res/drawable-xhdpi/ic_action_search.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-xhdpi/ic_launcher.png b/templates/projects/NewAndroidApplication/root/res/drawable-xhdpi/ic_launcher.png Binary files differindex 002e7b0..002e7b0 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/drawable-xhdpi/ic_launcher.png +++ b/templates/projects/NewAndroidApplication/root/res/drawable-xhdpi/ic_launcher.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/values-large/dimens.xml b/templates/projects/NewAndroidApplication/root/res/values-large/dimens.xml index d8cd7c2..d8cd7c2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/values-large/dimens.xml +++ b/templates/projects/NewAndroidApplication/root/res/values-large/dimens.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/values/dimens.xml b/templates/projects/NewAndroidApplication/root/res/values/dimens.xml index d95a70f..d95a70f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/values/dimens.xml +++ b/templates/projects/NewAndroidApplication/root/res/values/dimens.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/values/strings.xml.ftl b/templates/projects/NewAndroidApplication/root/res/values/strings.xml.ftl index 557e5c2..557e5c2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/values/strings.xml.ftl +++ b/templates/projects/NewAndroidApplication/root/res/values/strings.xml.ftl diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/values/styles.xml b/templates/projects/NewAndroidApplication/root/res/values/styles.xml index 5cd7c2f..5cd7c2f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/root/res/values/styles.xml +++ b/templates/projects/NewAndroidApplication/root/res/values/styles.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/template.xml b/templates/projects/NewAndroidApplication/template.xml index 84ba6c7..84ba6c7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/template.xml +++ b/templates/projects/NewAndroidApplication/template.xml diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/template_new_project.png b/templates/projects/NewAndroidApplication/template_new_project.png Binary files differindex 92e8556..92e8556 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/NewAndroidApplication/template_new_project.png +++ b/templates/projects/NewAndroidApplication/template_new_project.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/res/android-support-v4.jar.bin b/templates/resources/android-support-v4.jar.bin Binary files differindex d006198..d006198 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/res/android-support-v4.jar.bin +++ b/templates/resources/android-support-v4.jar.bin |