OSDN Git Service

merge from donut
authorJean-Baptiste Queru <jbq@google.com>
Thu, 3 Sep 2009 21:12:47 +0000 (14:12 -0700)
committerJean-Baptiste Queru <jbq@google.com>
Thu, 3 Sep 2009 21:12:47 +0000 (14:12 -0700)
396 files changed:
apps/Fallback/res/values-cs/strings.xml
apps/Fallback/res/values-da/strings.xml
apps/Fallback/res/values-de/strings.xml
apps/Fallback/res/values-el/strings.xml
apps/Fallback/res/values-es-rUS/strings.xml
apps/Fallback/res/values-es/strings.xml
apps/Fallback/res/values-fr/strings.xml
apps/Fallback/res/values-it/strings.xml
apps/Fallback/res/values-ja/strings.xml
apps/Fallback/res/values-ko/strings.xml
apps/Fallback/res/values-nb/strings.xml
apps/Fallback/res/values-nl/strings.xml
apps/Fallback/res/values-pl/strings.xml
apps/Fallback/res/values-pt-rPT/strings.xml
apps/Fallback/res/values-pt/strings.xml
apps/Fallback/res/values-ru/strings.xml
apps/Fallback/res/values-sv/strings.xml
apps/Fallback/res/values-tr/strings.xml
apps/Fallback/res/values-zh-rCN/strings.xml
apps/Fallback/res/values-zh-rTW/strings.xml
build/sdk.atree
build/tools/make_windows_sdk.sh
cmds/monkey/Android.mk
cmds/monkey/README.NETWORK.txt [new file with mode: 0644]
cmds/monkey/example_script.txt [new file with mode: 0644]
cmds/monkey/src/com/android/commands/monkey/Monkey.java
cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java
cmds/monkey/src/com/android/commands/monkey/MonkeyKeyEvent.java
cmds/monkey/src/com/android/commands/monkey/MonkeyNoopEvent.java [new file with mode: 0644]
cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetwork.java [new file with mode: 0644]
cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkVars.java [new file with mode: 0644]
cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
docs/SDK_RELEASE_NOTES
emulator/skins/HVGA/arrow_down.png
emulator/skins/HVGA/arrow_left.png
emulator/skins/HVGA/arrow_right.png
emulator/skins/HVGA/arrow_up.png
emulator/skins/HVGA/back.png [deleted file]
emulator/skins/HVGA/background_land.png [new file with mode: 0644]
emulator/skins/HVGA/background_port.png [new file with mode: 0644]
emulator/skins/HVGA/button.png [new file with mode: 0644]
emulator/skins/HVGA/controls.png [new file with mode: 0644]
emulator/skins/HVGA/device.png [deleted file]
emulator/skins/HVGA/end.png [deleted file]
emulator/skins/HVGA/home.png [deleted file]
emulator/skins/HVGA/key.png
emulator/skins/HVGA/keyboard.png
emulator/skins/HVGA/layout
emulator/skins/HVGA/menu.png [deleted file]
emulator/skins/HVGA/power.png [deleted file]
emulator/skins/HVGA/select.png
emulator/skins/HVGA/send.png [deleted file]
emulator/skins/HVGA/spacebar.png
emulator/skins/HVGA/volume_down.png [deleted file]
emulator/skins/HVGA/volume_up.png [deleted file]
emulator/skins/QVGA/.DS_Store [deleted file]
emulator/skins/QVGA/arrow_down.png
emulator/skins/QVGA/arrow_left.png
emulator/skins/QVGA/arrow_right.png
emulator/skins/QVGA/arrow_up.png
emulator/skins/QVGA/back.png [deleted file]
emulator/skins/QVGA/background_land.png [new file with mode: 0644]
emulator/skins/QVGA/background_port.png [new file with mode: 0644]
emulator/skins/QVGA/button.png [new file with mode: 0644]
emulator/skins/QVGA/controls.png [new file with mode: 0644]
emulator/skins/QVGA/device.png [deleted file]
emulator/skins/QVGA/end.png [deleted file]
emulator/skins/QVGA/home.png [deleted file]
emulator/skins/QVGA/key.png
emulator/skins/QVGA/keyboard.png
emulator/skins/QVGA/layout
emulator/skins/QVGA/menu.png [deleted file]
emulator/skins/QVGA/power.png [deleted file]
emulator/skins/QVGA/select.png
emulator/skins/QVGA/send.png [deleted file]
emulator/skins/QVGA/spacebar.png
emulator/skins/QVGA/volume_down.png [deleted file]
emulator/skins/QVGA/volume_up.png [deleted file]
emulator/skins/WQVGA432/arrow_down.png [new file with mode: 0644]
emulator/skins/WQVGA432/arrow_left.png [new file with mode: 0644]
emulator/skins/WQVGA432/arrow_right.png [new file with mode: 0644]
emulator/skins/WQVGA432/arrow_up.png [new file with mode: 0644]
emulator/skins/WQVGA432/background_land.png [new file with mode: 0644]
emulator/skins/WQVGA432/background_port.png [new file with mode: 0644]
emulator/skins/WQVGA432/button.png [new file with mode: 0644]
emulator/skins/WQVGA432/controls.png [new file with mode: 0644]
emulator/skins/WQVGA432/hardware.ini [new file with mode: 0644]
emulator/skins/WQVGA432/key.png [new file with mode: 0644]
emulator/skins/WQVGA432/keyboard.png [new file with mode: 0644]
emulator/skins/WQVGA432/layout [new file with mode: 0644]
emulator/skins/WQVGA432/select.png [new file with mode: 0644]
emulator/skins/WQVGA432/spacebar.png [new file with mode: 0644]
emulator/skins/WVGA/arrow_down.png [deleted file]
emulator/skins/WVGA/arrow_left.png [deleted file]
emulator/skins/WVGA/arrow_right.png [deleted file]
emulator/skins/WVGA/arrow_up.png [deleted file]
emulator/skins/WVGA/back.png [deleted file]
emulator/skins/WVGA/device.png [deleted file]
emulator/skins/WVGA/device.pxi [deleted file]
emulator/skins/WVGA/end.png [deleted file]
emulator/skins/WVGA/home.png [deleted file]
emulator/skins/WVGA/key.png [deleted file]
emulator/skins/WVGA/keyboard.png [deleted file]
emulator/skins/WVGA/menu.png [deleted file]
emulator/skins/WVGA/power.png [deleted file]
emulator/skins/WVGA/search.png [deleted file]
emulator/skins/WVGA/select.png [deleted file]
emulator/skins/WVGA/send.png [deleted file]
emulator/skins/WVGA/spacebar.png [deleted file]
emulator/skins/WVGA/volume_down.png [deleted file]
emulator/skins/WVGA/volume_up.png [deleted file]
emulator/skins/WVGA800/arrow_down.png [new file with mode: 0644]
emulator/skins/WVGA800/arrow_left.png [new file with mode: 0644]
emulator/skins/WVGA800/arrow_right.png [new file with mode: 0644]
emulator/skins/WVGA800/arrow_up.png [new file with mode: 0644]
emulator/skins/WVGA800/background_land.png [new file with mode: 0644]
emulator/skins/WVGA800/background_port.png [new file with mode: 0644]
emulator/skins/WVGA800/button.png [new file with mode: 0644]
emulator/skins/WVGA800/controls.png [new file with mode: 0644]
emulator/skins/WVGA800/hardware.ini [moved from emulator/skins/WVGA/hardware.ini with 100% similarity]
emulator/skins/WVGA800/key.png [new file with mode: 0644]
emulator/skins/WVGA800/keyboard.png [new file with mode: 0644]
emulator/skins/WVGA800/layout [moved from emulator/skins/WVGA/layout with 50% similarity]
emulator/skins/WVGA800/select.png [new file with mode: 0644]
emulator/skins/WVGA800/spacebar.png [new file with mode: 0644]
emulator/skins/WVGA854/arrow_down.png [new file with mode: 0644]
emulator/skins/WVGA854/arrow_left.png [new file with mode: 0644]
emulator/skins/WVGA854/arrow_right.png [new file with mode: 0644]
emulator/skins/WVGA854/arrow_up.png [new file with mode: 0644]
emulator/skins/WVGA854/background_land.png [new file with mode: 0644]
emulator/skins/WVGA854/background_port.png [new file with mode: 0644]
emulator/skins/WVGA854/button.png [new file with mode: 0644]
emulator/skins/WVGA854/controls.png [new file with mode: 0644]
emulator/skins/WVGA854/hardware.ini [new file with mode: 0644]
emulator/skins/WVGA854/key.png [new file with mode: 0644]
emulator/skins/WVGA854/keyboard.png [new file with mode: 0644]
emulator/skins/WVGA854/layout [new file with mode: 0644]
emulator/skins/WVGA854/select.png [new file with mode: 0644]
emulator/skins/WVGA854/spacebar.png [new file with mode: 0644]
host/windows/.gitignore
host/windows/prebuilt/usb/AdbWinApi.dll
host/windows/prebuilt/usb/AdbWinUsbApi.dll [new file with mode: 0755]
host/windows/prebuilt/usb/Android.mk
host/windows/usb/android_winusb.inf
host/windows/usb/api/AdbWinApi.cpp
host/windows/usb/api/SOURCES
host/windows/usb/api/adb_api.cpp
host/windows/usb/api/adb_api.h
host/windows/usb/api/adb_endpoint_object.h
host/windows/usb/api/adb_interface.h
host/windows/usb/api/adb_io_completion.h
host/windows/usb/api/adb_object_handle.h
host/windows/usb/api/adb_winusb_api.h [new file with mode: 0755]
host/windows/usb/api/stdafx.h
host/windows/usb/winusb/AdbWinUsbApi.cpp [new file with mode: 0755]
host/windows/usb/winusb/AdbWinUsbApi.def [new file with mode: 0755]
host/windows/usb/winusb/AdbWinUsbApi.rc [new file with mode: 0755]
host/windows/usb/winusb/BUILDME.TXT [new file with mode: 0755]
host/windows/usb/winusb/MAKEFILE [new file with mode: 0755]
host/windows/usb/winusb/Resource.h [new file with mode: 0755]
host/windows/usb/winusb/SOURCES [new file with mode: 0755]
host/windows/usb/winusb/adb_winusb_endpoint_object.cpp [moved from host/windows/usb/api/adb_winusb_endpoint_object.cpp with 93% similarity]
host/windows/usb/winusb/adb_winusb_endpoint_object.h [moved from host/windows/usb/api/adb_winusb_endpoint_object.h with 81% similarity]
host/windows/usb/winusb/adb_winusb_interface.cpp [moved from host/windows/usb/api/adb_winusb_interface.cpp with 95% similarity]
host/windows/usb/winusb/adb_winusb_interface.h [moved from host/windows/usb/api/adb_winusb_interface.h with 84% similarity]
host/windows/usb/winusb/adb_winusb_io_completion.cpp [moved from host/windows/usb/api/adb_winusb_io_completion.cpp with 89% similarity]
host/windows/usb/winusb/adb_winusb_io_completion.h [moved from host/windows/usb/api/adb_winusb_io_completion.h with 75% similarity]
host/windows/usb/winusb/stdafx.cpp [new file with mode: 0755]
host/windows/usb/winusb/stdafx.h [new file with mode: 0755]
ndk/build/tools/make-release.sh
ndk/docs/CHANGES.TXT
pdk/docs/community/index.jd [new file with mode: 0644]
pdk/docs/guide/audio.jd
pdk/docs/guide/bluetooth.jd
pdk/docs/guide/bluetooth/bluetooth_process.jd
pdk/docs/guide/bring_up.jd
pdk/docs/guide/build_cookbook.jd
pdk/docs/guide/build_new_device.jd
pdk/docs/guide/build_system.jd
pdk/docs/guide/camera.jd
pdk/docs/guide/customization.jd
pdk/docs/guide/dalvik.jd
pdk/docs/guide/debugging_gdb.jd
pdk/docs/guide/debugging_native.jd
pdk/docs/guide/display_drivers.jd
pdk/docs/guide/getting_source_code.jd
pdk/docs/guide/gps.jd
pdk/docs/guide/group__memory.jd
pdk/docs/guide/group__networking.jd
pdk/docs/guide/index.jd
pdk/docs/guide/instrumentation_framework.jd
pdk/docs/guide/instrumentation_testing.jd
pdk/docs/guide/intro_source_code.jd
pdk/docs/guide/keymaps_keyboard_input.jd
pdk/docs/guide/lights.jd
pdk/docs/guide/power_management.jd
pdk/docs/guide/release_keys.jd
pdk/docs/guide/sensors.jd
pdk/docs/guide/source_setup_guide.jd
pdk/docs/guide/stk.jd
pdk/docs/guide/system_requirements.jd
pdk/docs/guide/tcpdump.jd
pdk/docs/guide/telephony.jd
pdk/docs/guide/wifi.jd
pdk/docs/index.jd [new file with mode: 0644]
pdk/docs/licenses/index.jd [new file with mode: 0644]
pdk/docs/licenses/licenses_toc.cs [new file with mode: 0644]
pdk/docs/releases/index.jd [new file with mode: 0644]
pdk/docs/releases/releases_toc.cs [new file with mode: 0644]
samples/ApiDemos/AndroidManifest.xml
samples/ApiDemos/_index.html
samples/ApiDemos/default.properties
samples/ApiDemos/res/drawable-hdpi/logo240dpi.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-hdpi/npatch240dpi.9.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-hdpi/reslogo240dpi.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-hdpi/smlnpatch240dpi.9.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-hdpi/stylogo240dpi.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-ldpi/logo120dpi.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-ldpi/npatch120dpi.9.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-ldpi/reslogo120dpi.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-ldpi/smlnpatch120dpi.9.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-ldpi/stylogo120dpi.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-nodpi/logonodpi120.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-nodpi/logonodpi160.png [new file with mode: 0644]
samples/ApiDemos/res/drawable-nodpi/logonodpi240.png [new file with mode: 0644]
samples/ApiDemos/res/drawable/logo160dpi.png [new file with mode: 0644]
samples/ApiDemos/res/drawable/npatch160dpi.9.png [new file with mode: 0644]
samples/ApiDemos/res/drawable/reslogo160dpi.png [new file with mode: 0644]
samples/ApiDemos/res/drawable/smlnpatch160dpi.9.png [new file with mode: 0644]
samples/ApiDemos/res/drawable/stylogo160dpi.png [new file with mode: 0644]
samples/ApiDemos/res/layout/density_image_views.xml [new file with mode: 0644]
samples/ApiDemos/res/layout/density_styled_image_views.xml [new file with mode: 0644]
samples/ApiDemos/res/values-large-long/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-large-notlong/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-large/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-long/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-normal-long/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-normal-notlong/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-normal/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-notlong/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-small-long/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-small-notlong/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values-small/strings.xml [new file with mode: 0644]
samples/ApiDemos/res/values/strings.xml
samples/ApiDemos/res/values/styles.xml
samples/ApiDemos/src/com/example/android/apis/ApiDemosApplication.java
samples/ApiDemos/src/com/example/android/apis/app/IncomingMessageView.java
samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.java
samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleBroadcastReceiver.java
samples/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.java
samples/ApiDemos/src/com/example/android/apis/graphics/ColorPickerDialog.java
samples/ApiDemos/src/com/example/android/apis/graphics/DensityActivity.java [new file with mode: 0644]
samples/ApiDemos/src/com/example/android/apis/graphics/kube/GLColor.java
samples/ApiDemos/src/com/example/android/apis/graphics/kube/GLVertex.java
samples/ApiDemos/src/com/example/android/apis/graphics/kube/KubeRenderer.java
samples/ApiDemos/src/com/example/android/apis/media/MediaPlayerDemo.java
samples/ApiDemos/src/com/example/android/apis/media/MediaPlayerDemo_Audio.java
samples/ApiDemos/src/com/example/android/apis/media/MediaPlayerDemo_Video.java
samples/ApiDemos/src/com/example/android/apis/media/VideoViewDemo.java
samples/ApiDemos/src/com/example/android/apis/view/BaselineNested1.java
samples/ApiDemos/src/com/example/android/apis/view/BaselineNested2.java
samples/ApiDemos/src/com/example/android/apis/view/BaselineNested3.java
samples/ApiDemos/src/com/example/android/apis/view/Focus2.java
samples/ApiDemos/src/com/example/android/apis/view/Focus3.java
samples/ApiDemos/src/com/example/android/apis/view/InternalSelectionView.java
samples/ApiDemos/src/com/example/android/apis/view/LayoutAnimation2.java
samples/ApiDemos/src/com/example/android/apis/view/LayoutAnimation3.java
samples/ApiDemos/src/com/example/android/apis/view/LayoutAnimation5.java
samples/ApiDemos/src/com/example/android/apis/view/LayoutAnimation7.java
samples/ApiDemos/src/com/example/android/apis/view/List5.java
samples/Home/_index.html [new file with mode: 0644]
samples/JetBoy/_index.html [new file with mode: 0644]
samples/LunarLander/_index.html
samples/LunarLander/sample_lunarlander.png [deleted file]
samples/NotePad/_index.html
samples/NotePad/sample_note.png [deleted file]
samples/NotePad/sample_notepad.png [deleted file]
samples/SearchableDictionary/Android.mk [new file with mode: 0755]
samples/SearchableDictionary/AndroidManifest.xml [new file with mode: 0644]
samples/SearchableDictionary/_index.html [new file with mode: 0644]
samples/SearchableDictionary/res/drawable/ic_dictionary.png [new file with mode: 0644]
samples/SearchableDictionary/res/layout/main.xml [new file with mode: 0644]
samples/SearchableDictionary/res/layout/word.xml [new file with mode: 0644]
samples/SearchableDictionary/res/raw/definitions.txt [new file with mode: 0644]
samples/SearchableDictionary/res/values/strings.xml [new file with mode: 0644]
samples/SearchableDictionary/res/xml/searchable.xml [new file with mode: 0644]
samples/SearchableDictionary/src/com/example/android/searchabledict/Dictionary.java [new file with mode: 0644]
samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryProvider.java [new file with mode: 0644]
samples/SearchableDictionary/src/com/example/android/searchabledict/SearchableDictionary.java [new file with mode: 0644]
samples/SearchableDictionary/src/com/example/android/searchabledict/WordActivity.java [new file with mode: 0644]
samples/Snake/_index.html [new file with mode: 0644]
samples/SoftKeyboard/_index.html [new file with mode: 0644]
testrunner/test_defs.xml
testrunner/test_defs/native_test.py
tools/androidprefs/src/com/android/prefs/AndroidLocation.java
tools/anttasks/src/com/android/ant/ApkBuilderTask.java
tools/eclipse/features/com.android.ide.eclipse.adt/feature.xml
tools/eclipse/features/com.android.ide.eclipse.ddms/feature.xml
tools/eclipse/features/com.android.ide.eclipse.tests/feature.xml
tools/eclipse/plugins/com.android.ide.eclipse.adt/.classpath
tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutCreatorDialog.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringRefactoring.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/CountryCodeQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/KeyboardStateQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/LanguageQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationMethodQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NetworkCodeQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/PixelDensityQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/RegionQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ResourceQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenDimensionQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenOrientationQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenRatioQualifier.java [new file with mode: 0644]
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenSizeQualifier.java [new file with mode: 0644]
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/TextInputMethodQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/TouchScreenQualifier.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/VersionQualifier.java [new file with mode: 0644]
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java
tools/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF
tools/eclipse/plugins/com.android.ide.eclipse.tests/.classpath
tools/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
tools/eclipse/plugins/com.android.ide.eclipse.tests/README.txt
tools/eclipse/plugins/com.android.ide.eclipse.tests/build.properties
tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/FuncTestCase.java
tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/FuncTests.java
tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java [new file with mode: 0644]
tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java
tools/eclipse/sites/external/site.xml
tools/eclipse/sites/internal/site.xml
tools/monkeyrunner/Android.mk [new file with mode: 0644]
tools/monkeyrunner/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
tools/monkeyrunner/NOTICE [new file with mode: 0644]
tools/monkeyrunner/etc/Android.mk [new file with mode: 0644]
tools/monkeyrunner/etc/manifest.txt [new file with mode: 0644]
tools/monkeyrunner/etc/monkeyrunner [new file with mode: 0755]
tools/monkeyrunner/src/Android.mk [new file with mode: 0644]
tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java [new file with mode: 0644]
tools/monkeyrunner/src/com/android/monkeyrunner/ScriptRunner.java [new file with mode: 0644]
tools/scripts/AndroidManifest.alias.template
tools/scripts/alias.template
tools/scripts/alias_rules.xml
tools/scripts/android_rules.xml
tools/scripts/build.alias.template
tools/scripts/build.template
tools/scripts/doc_source.properties
tools/scripts/java_tests_file.template
tools/scripts/platform_source.properties
tools/scripts/tools_source.properties
tools/sdkmanager/app/etc/android.bat
tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AndroidVersion.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSources.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd
tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AdbWrapper.java [new file with mode: 0755]
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateChooserDialog.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png [new file with mode: 0755]
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java
tools/sdkstats/src/com/android/sdkstats/SdkStatsService.java

index b9d34f9..1fc121c 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Záloha"</string>
-    <string name="title">"Akce není podporována"</string>
-    <string name="error">"Tato akce není momentálně podporována."</string>
+    <string name="appTitle" msgid="161410001913116606">"Záloha"</string>
+    <string name="title" msgid="8156274565006125136">"Akce není podporována"</string>
+    <string name="error" msgid="6539615832923362301">"Tato akce není momentálně podporována."</string>
 </resources>
index b3dfa63..4584e61 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Reserve"</string>
-    <string name="title">"Ikke understøttet handling"</string>
-    <string name="error">"Handlingen er ikke understøttet i øjeblikket."</string>
+    <string name="appTitle" msgid="161410001913116606">"Reserve"</string>
+    <string name="title" msgid="8156274565006125136">"Ikke understøttet handling"</string>
+    <string name="error" msgid="6539615832923362301">"Handlingen er ikke understøttet i øjeblikket."</string>
 </resources>
index 8d59ddf..90bbb16 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Fallback"</string>
-    <string name="title">"Nicht unterstützte Aktion"</string>
-    <string name="error">"Diese Aktion wird zurzeit nicht unterstützt."</string>
+    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+    <string name="title" msgid="8156274565006125136">"Nicht unterstützte Aktion"</string>
+    <string name="error" msgid="6539615832923362301">"Diese Aktion wird zurzeit nicht unterstützt."</string>
 </resources>
index fecaf4a..b985af7 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Εναλλακτική"</string>
-    <string name="title">"Ενέργεια που δεν υποστηρίζεται"</string>
-    <string name="error">"Αυτή η ενέργεια δεν υποστηρίζεται αυτήν τη στιγμή."</string>
+    <string name="appTitle" msgid="161410001913116606">"Εναλλακτική"</string>
+    <string name="title" msgid="8156274565006125136">"Ενέργεια που δεν υποστηρίζεται"</string>
+    <string name="error" msgid="6539615832923362301">"Αυτή η ενέργεια δεν υποστηρίζεται αυτήν τη στιγμή."</string>
 </resources>
index 0ce5751..d15a287 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Fallback"</string>
-    <string name="title">"Acción no admitida"</string>
-    <string name="error">"Esa acción no se admite actualmente."</string>
+    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+    <string name="title" msgid="8156274565006125136">"Acción no admitida"</string>
+    <string name="error" msgid="6539615832923362301">"Esa acción no se admite actualmente."</string>
 </resources>
index 0ce5751..d15a287 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Fallback"</string>
-    <string name="title">"Acción no admitida"</string>
-    <string name="error">"Esa acción no se admite actualmente."</string>
+    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+    <string name="title" msgid="8156274565006125136">"Acción no admitida"</string>
+    <string name="error" msgid="6539615832923362301">"Esa acción no se admite actualmente."</string>
 </resources>
index 024ae42..df1e299 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Application de secours"</string>
-    <string name="title">"Action non prise en charge"</string>
-    <string name="error">"Cette action n\'est actuellement pas prise en charge."</string>
+    <string name="appTitle" msgid="161410001913116606">"Application de secours"</string>
+    <string name="title" msgid="8156274565006125136">"Action non prise en charge"</string>
+    <string name="error" msgid="6539615832923362301">"Cette action n\'est actuellement pas prise en charge."</string>
 </resources>
index d216e59..2d08c1b 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Fallback"</string>
-    <string name="title">"Azione non supportata"</string>
-    <string name="error">"L\'azione non è al momento supportata."</string>
+    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+    <string name="title" msgid="8156274565006125136">"Azione non supportata"</string>
+    <string name="error" msgid="6539615832923362301">"L\'azione non è al momento supportata."</string>
 </resources>
index 79aeb42..f220909 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Fallback"</string>
-    <string name="title">"サポートされていない操作"</string>
-    <string name="error">"現在サポートされていない操作です。"</string>
+    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+    <string name="title" msgid="8156274565006125136">"サポートされていない操作"</string>
+    <string name="error" msgid="6539615832923362301">"現在サポートされていない操作です。"</string>
 </resources>
index ec1c330..2c2973f 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"폴백"</string>
-    <string name="title">"지원되지 않는 작업"</string>
-    <string name="error">"이 작업은 현재 지원되지 않습니다."</string>
+    <string name="appTitle" msgid="161410001913116606">"폴백"</string>
+    <string name="title" msgid="8156274565006125136">"지원되지 않는 작업"</string>
+    <string name="error" msgid="6539615832923362301">"이 작업은 현재 지원되지 않습니다."</string>
 </resources>
index 6fed660..02814e6 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Fallback"</string>
-    <string name="title">"Ustøttet handling"</string>
-    <string name="error">"Denne handlingen er ikke støttet nå."</string>
+    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+    <string name="title" msgid="8156274565006125136">"Ustøttet handling"</string>
+    <string name="error" msgid="6539615832923362301">"Denne handlingen er ikke støttet nå."</string>
 </resources>
index f347964..8989efd 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Reserve"</string>
-    <string name="title">"Niet-ondersteunde actie"</string>
-    <string name="error">"Die actie wordt momenteel niet ondersteund."</string>
+    <string name="appTitle" msgid="161410001913116606">"Reserve"</string>
+    <string name="title" msgid="8156274565006125136">"Niet-ondersteunde actie"</string>
+    <string name="error" msgid="6539615832923362301">"Die actie wordt momenteel niet ondersteund."</string>
 </resources>
index 73a176a..5740498 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Wycofanie"</string>
-    <string name="title">"Nieobsługiwana czynność"</string>
-    <string name="error">"Ta czynność nie jest aktualnie obsługiwana."</string>
+    <string name="appTitle" msgid="161410001913116606">"Wycofanie"</string>
+    <string name="title" msgid="8156274565006125136">"Nieobsługiwana czynność"</string>
+    <string name="error" msgid="6539615832923362301">"Ta czynność nie jest aktualnie obsługiwana."</string>
 </resources>
index 3c7ec9d..b226ea5 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Fallback"</string>
-    <string name="title">"Acção não suportada"</string>
-    <string name="error">"Esta·acção·ainda·não·é·suportada."</string>
+    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+    <string name="title" msgid="8156274565006125136">"Acção não suportada"</string>
+    <string name="error" msgid="6539615832923362301">"Esta acção ainda não é suportada."</string>
 </resources>
index 08a3faa..395004c 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Fallback"</string>
-    <string name="title">"Ação não suportada"</string>
-    <string name="error">"Essa ação não é suportada no momento."</string>
+    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+    <string name="title" msgid="8156274565006125136">"Ação não suportada"</string>
+    <string name="error" msgid="6539615832923362301">"Essa ação não é suportada no momento."</string>
 </resources>
index 24c3480..084c3f3 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Переход в обходной режим"</string>
-    <string name="title">"Неподдерживаемое действие"</string>
-    <string name="error">"В настоящее время это действие не поддерживается."</string>
+    <string name="appTitle" msgid="161410001913116606">"Переход в обходной режим"</string>
+    <string name="title" msgid="8156274565006125136">"Неподдерживаемое действие"</string>
+    <string name="error" msgid="6539615832923362301">"В настоящее время это действие не поддерживается."</string>
 </resources>
index 9dae10d..224d946 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Reserv"</string>
-    <string name="title">"Åtgärden stöds inte"</string>
-    <string name="error">"Den här åtgärden stöds inte för närvarande."</string>
+    <string name="appTitle" msgid="161410001913116606">"Reserv"</string>
+    <string name="title" msgid="8156274565006125136">"Åtgärden stöds inte"</string>
+    <string name="error" msgid="6539615832923362301">"Den här åtgärden stöds inte för närvarande."</string>
 </resources>
index 27b860a..9c7ed15 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"Fallback"</string>
-    <string name="title">"Desteklenmeyen işlem"</string>
-    <string name="error">"Bu işlem şu an desteklenmiyor."</string>
+    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+    <string name="title" msgid="8156274565006125136">"Desteklenmeyen işlem"</string>
+    <string name="error" msgid="6539615832923362301">"Bu işlem şu an desteklenmiyor."</string>
 </resources>
index e6cfde1..5b1fbf8 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"后备"</string>
-    <string name="title">"不支持此操作"</string>
-    <string name="error">"目前不支持该操作。"</string>
+    <string name="appTitle" msgid="161410001913116606">"后备"</string>
+    <string name="title" msgid="8156274565006125136">"不支持此操作"</string>
+    <string name="error" msgid="6539615832923362301">"目前不支持该操作。"</string>
 </resources>
index 52afdbe..04d7f4f 100644 (file)
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle">"備用"</string>
-    <string name="title">"不支援的操作"</string>
-    <string name="error">"目前不支援此操作。"</string>
+    <string name="appTitle" msgid="161410001913116606">"備用"</string>
+    <string name="title" msgid="8156274565006125136">"不支援的操作"</string>
+    <string name="error" msgid="6539615832923362301">"目前不支援此操作。"</string>
 </resources>
index 4b7b4a9..16d65de 100644 (file)
@@ -31,6 +31,7 @@ bin/sqlite3 tools/sqlite3
 bin/dmtracedump tools/dmtracedump
 bin/hprof-conv tools/hprof-conv
 bin/mksdcard tools/mksdcard
+bin/zipalign tools/zipalign
 
 # the uper-jar file that apps link against
 out/target/common/obj/PACKAGING/android_jar_intermediates/android.jar platforms/${PLATFORM_NAME}/android.jar
@@ -39,7 +40,7 @@ out/target/common/obj/PACKAGING/android_jar_intermediates/android.jar platforms/
 framework/org.eclipse.core.commands_3.4.0.I20080509-2000.jar tools/lib/org.eclipse.core.commands_3.4.0.I20080509-2000.jar
 framework/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar tools/lib/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar
 framework/org.eclipse.jface_3.4.2.M20090107-0800.jar tools/lib/org.eclipse.jface_3.4.2.M20090107-0800.jar
-
+framework/osgi.jar tools/lib/osgi.jar
 
 sdk/sdk-build.prop platforms/${PLATFORM_NAME}/build.prop
 development/tools/scripts/plugin.prop tools/lib/plugin.prop
@@ -49,7 +50,6 @@ obj/framework.aidl platforms/${PLATFORM_NAME}/framework.aidl
 
 # sdk scripts
 development/tools/scripts/AndroidManifest.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.template
-development/tools/scripts/AndroidManifest.alias.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.alias.template
 development/tools/scripts/AndroidManifest.tests.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.tests.template
 development/tools/scripts/iml.template platforms/${PLATFORM_NAME}/templates/iml.template
 development/tools/scripts/ipr.template platforms/${PLATFORM_NAME}/templates/ipr.template
@@ -58,11 +58,8 @@ development/tools/scripts/java_file.template platforms/${PLATFORM_NAME}/template
 development/tools/scripts/java_tests_file.template platforms/${PLATFORM_NAME}/templates/java_tests_file.template
 development/tools/scripts/layout.template platforms/${PLATFORM_NAME}/templates/layout.template
 development/tools/scripts/strings.template platforms/${PLATFORM_NAME}/templates/strings.template
-development/tools/scripts/alias.template platforms/${PLATFORM_NAME}/templates/alias.template
 development/tools/scripts/android_rules.xml platforms/${PLATFORM_NAME}/templates/android_rules.xml
-development/tools/scripts/alias_rules.xml platforms/${PLATFORM_NAME}/templates/alias_rules.xml
 development/tools/scripts/build.template tools/lib/build.template
-development/tools/scripts/build.alias.template tools/lib/build.alias.template
 
 # emacs support
 development/tools/scripts/android.el tools/lib/android.el
@@ -78,6 +75,7 @@ development/samples/SkeletonApp platforms/${PLATFORM_NAME}/samples/SkeletonApp
 development/samples/Snake platforms/${PLATFORM_NAME}/samples/Snake
 development/samples/SoftKeyboard platforms/${PLATFORM_NAME}/samples/SoftKeyboard
 development/samples/JetBoy platforms/${PLATFORM_NAME}/samples/JetBoy
+development/samples/SearchableDictionary platforms/${PLATFORM_NAME}/samples/SearchableDictionary
 
 # dx
 bin/dx platforms/${PLATFORM_NAME}/tools/dx
@@ -146,9 +144,10 @@ prebuilt/android-arm/kernel/kernel-qemu platforms/${PLATFORM_NAME}/images/kernel
 external/qemu/android/avd/hardware-properties.ini tools/lib/hardware-properties.ini
 
 # emulator skins
-development/emulator/skins/HVGA   platforms/${PLATFORM_NAME}/skins/HVGA
-development/emulator/skins/QVGA   platforms/${PLATFORM_NAME}/skins/QVGA
-development/emulator/skins/WVGA   platforms/${PLATFORM_NAME}/skins/WVGA
+development/emulator/skins/HVGA     platforms/${PLATFORM_NAME}/skins/HVGA
+development/emulator/skins/QVGA     platforms/${PLATFORM_NAME}/skins/QVGA
+development/emulator/skins/WVGA800  platforms/${PLATFORM_NAME}/skins/WVGA800
+development/emulator/skins/WVGA854  platforms/${PLATFORM_NAME}/skins/WVGA854
 
 # NOTICE files are copied by build/core/Makefile
 development/tools/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/templates/NOTICE.txt
index 52c2f6c..d694ea4 100755 (executable)
@@ -25,25 +25,26 @@ DIST_DIR="$2"
 TEMP_DIR="$3"
 [ -z "$TEMP_DIR" ] && TEMP_DIR=${TMP:-/tmp}
 
-
 function die() {
-  echo "Error:" $*
-  echo "Aborting"
-  exit 1
+    echo "Error:" $*
+    echo "Aborting"
+    exit 1
 }
 
 function usage() {
-  echo "Usage: ${PROG_NAME} linux_or_mac_sdk.zip output_dir [temp_dir]"
-  echo "If temp_dir is not given, \$TMP is used. If that's missing, /tmp is used."
-  status
-  exit 2
+    local NAME
+    NAME=`basename ${PROG_NAME}`
+    echo "Usage: ${NAME} linux_or_mac_sdk.zip output_dir [temp_dir]"
+    echo "If temp_dir is not given, \$TMP is used. If that's missing, /tmp is used."
+    status
+    exit 2
 }
 
 function status() {
-  echo "Current values:"
-  echo "- Input  SDK: ${SDK_ZIP:-missing}"
-  echo "- Output dir: ${DIST_DIR:-missing}"
-  echo "- Temp   dir: ${TEMP_DIR:-missing}"
+    echo "Current values:"
+    echo "- Input  SDK: ${SDK_ZIP:-missing}"
+    echo "- Output dir: ${DIST_DIR:-missing}"
+    echo "- Temp   dir: ${TEMP_DIR:-missing}"
 }
 
 function check() {
@@ -92,7 +93,14 @@ function build() {
     make -j 4 emulator || die "Build failed"
     # Disable parallel build: it generates "permission denied" issues when
     # multiple "ar.exe" are running in parallel.
-    make prebuilt adb fastboot aidl aapt dexdump dmtracedump hprof-conv mksdcard sqlite3 \
+    make aapt adb aidl \
+        prebuilt \
+        dexdump dmtracedump \
+        fastboot \
+        hprof-conv \
+        mksdcard \
+        sqlite3 \
+        zipalign \
         || die "Build failed"
 }
 
@@ -124,12 +132,16 @@ function package() {
         "Instead found " $THE_PLATFORM
     [[ -d "$PLATFORM_TOOLS" ]] || die "Missing folder $PLATFORM_TOOLS."
 
+    # Package USB Driver
+    if type package_usb_driver 2>&1 | grep -q function ; then
+        package_usb_driver $TEMP_SDK_DIR
+    fi
 
     # Remove obsolete stuff from tools & platform
     TOOLS="$TEMP_SDK_DIR/tools"
     LIB="$TEMP_SDK_DIR/tools/lib"
     rm -v "$TOOLS"/{adb,android,apkbuilder,ddms,dmtracedump,draw9patch,emulator}
-    rm -v "$TOOLS"/{hierarchyviewer,hprof-conv,mksdcard,sqlite3,traceview}
+    rm -v "$TOOLS"/{hierarchyviewer,hprof-conv,mksdcard,sqlite3,traceview,zipalign}
     rm -v "$LIB"/*/swt.jar
     rm -v "$PLATFORM_TOOLS"/{aapt,aidl,dx,dexdump}
 
@@ -183,8 +195,9 @@ function package() {
 
     # Copy or move platform specific tools to the default platform.
     cp -v dalvik/dx/etc/dx.bat "$PLATFORM_TOOLS"/
-    # Note: mgwz.dll must be in same folder than aapt.exe
-    mv -v "$TOOLS"/{aapt.exe,aidl.exe,dexdump.exe,mgwz.dll} "$PLATFORM_TOOLS"/
+    mv -v "$TOOLS"/{aapt.exe,aidl.exe,dexdump.exe} "$PLATFORM_TOOLS"/
+    # Note: mgwz.dll must be both in SDK/tools for zipalign and in SDK/platform/XYZ/tools/ for aapt
+    cp -v "$TOOLS"/mgwz.dll "$PLATFORM_TOOLS"/
 
     # Fix EOL chars to make window users happy - fix all files at the top level only
     # as well as all batch files including those in platforms/<name>/tools/
@@ -200,14 +213,14 @@ function package() {
     # Now move the final zip from the temp dest to the final dist dir
     mv -v "$TEMP_DIR/$DEST_NAME_ZIP" "$DIST_DIR/$DEST_NAME_ZIP"
 
+    # We want fastboot and adb (and its DLLs) next to the new SDK
+    for i in fastboot.exe adb.exe AdbWinApi.dll AdbWinUsbApi.dll; do
+        cp -vf out/host/windows-x86/bin/$i "$DIST_DIR"/$i
+    done
+
     echo "Done"
     echo
     echo "Resulting SDK is in $DIST_DIR/$DEST_NAME_ZIP"
-
-    # We want fastboot and adb next to the new SDK
-    for i in fastboot.exe adb.exe AdbWinApi.dll; do
-        mv -vf out/host/windows-x86/bin/$i "$DIST_DIR"/$i
-    done
 }
 
 check
index 6bedc43..ba9cf04 100644 (file)
@@ -7,6 +7,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files)
 LOCAL_MODULE := monkey
 include $(BUILD_JAVA_LIBRARY)
 
+################################################################
 include $(CLEAR_VARS)
 ALL_PREBUILT += $(TARGET_OUT)/bin/monkey
 $(TARGET_OUT)/bin/monkey : $(LOCAL_PATH)/monkey | $(ACP)
diff --git a/cmds/monkey/README.NETWORK.txt b/cmds/monkey/README.NETWORK.txt
new file mode 100644 (file)
index 0000000..4e78b6c
--- /dev/null
@@ -0,0 +1,129 @@
+SIMPLE PROTOCOL FOR AUTOMATED NETWORK CONTROL
+
+The Simple Protocol for Automated Network Control was designed to be a
+low-level way to programmability inject KeyEvents and MotionEvents
+into the input system.  The idea is that a process will run on a host
+computer that will support higher-level operations (like conditionals,
+etc.) and will talk (via TCP over ADB) to the device in Simple
+Protocol for Automated Network Control.  For security reasons, the
+Monkey only binds to localhost, so you will need to use adb to setup
+port forwarding to actually talk to the device.
+
+INITIAL SETUP
+
+Setup port forwarding from a local port on your machine to a port on
+the device:
+
+$ adb forward tcp:1080 tcp:1080
+
+Start the monkey server
+
+$ adb shell monkey --port 1080
+
+Now you're ready to run commands
+
+COMMAND LIST
+
+Individual commands are separated by newlines.  The Monkey will
+respond to every command with a line starting with OK for commands
+that executed without a problem, or a line starting with ERROR for
+commands that had problems being run.  For commands that return a
+value, that value is returned on the same line as the OK or ERROR
+response.  The value is everything after (but not include) the colon
+on that line.  For ERROR values, this could be a message indicating
+what happened.  A possible example:
+
+key down menu
+OK
+touch monkey
+ERROR: monkey not a number
+getvar sdk
+OK: donut
+getvar foo
+ERROR: no such var
+
+The complete list of commands follows:
+
+key [down|up] keycode
+
+This command injects KeyEvent's into the input system.  The keycode
+parameter refers to the KEYCODE list in the KeyEvent class
+(http://developer.android.com/reference/android/view/KeyEvent.html).
+The format of that parameter is quite flexible.  Using the menu key as
+an example, it can be 82 (the integer value of the keycode),
+KEYCODE_MENU (the name of the keycode), or just menu (and the Monkey
+will add the KEYCODE part).  Do note that this last part doesn't work
+for things like KEYCODE_1 for obvious reasons.
+
+Note that sending a full button press requires sending both the down
+and the up event for that key
+
+touch [down|up|move] x y
+
+This command injects a MotionEvent into the input system that
+simulates a user touching the touchscreen (or a pointer event).  x and
+y specify coordinates on the display (0 0 being the upper left) for
+the touch event to happen.  Just like key events, touch events at a
+single location require both a down and an up.  To simulate dragging,
+send a "touch down", then a series of "touch move" events (to simulate
+the drag), followed by a "touch up" at the final location.
+
+trackball dx dy
+
+This command injects a MotionEvent into the input system that
+simulates a user using the trackball. dx and dy indicates the amount
+of change in the trackball location (as opposed to exact coordinates
+that the touch events use)
+
+flip [open|close]
+
+This simulates the opening or closing the keyboard (like on dream).
+
+wake
+
+This command will wake the device up from sleep and allow user input.
+
+tap x y
+The tap command is a shortcut for the touch command.  It will
+automatically send both the up and the down event.
+
+press keycode
+
+The press command is a shortcut for the key command.  The keycode
+paramter works just like the key command and will automatically send
+both the up and the down event.
+
+type string
+
+This command will simulate a user typing the given string on the
+keyboard by generating the proper KeyEvents.
+
+listvar
+
+This command lists all the vars that the monkey knows about.  They are
+returned as a whitespace separated list.
+
+getvar varname
+
+This command returns the value of the given var.  listvar can be used
+to find out what vars are supported.
+
+quit
+
+Fully quit the monkey and accept no new sessions.
+
+done
+
+Close the current session and allow a new session to connect
+
+OTHER NOTES
+
+There are some convenience features added to allow running without
+needing a host process.
+
+Lines starting with a # character are considered comments.  The Monkey
+eats them and returns no indication that it did anything (no ERROR and
+no OK).
+
+You can put the Monkey to sleep by using the "sleep" command with a
+single argument, how many ms to sleep.
diff --git a/cmds/monkey/example_script.txt b/cmds/monkey/example_script.txt
new file mode 100644 (file)
index 0000000..5c1c61d
--- /dev/null
@@ -0,0 +1,57 @@
+# Touch the android
+touch down 160 200
+touch up 160 200
+sleep 1000
+
+# Hit Next
+touch down 300 450
+touch up 300 450
+sleep 1000
+
+# Hit Next
+touch down 300 450
+touch up 300 450
+sleep 1000
+
+# Hit Next
+touch down 300 450
+touch up 300 450
+sleep 1000
+
+# Go down and select the account username
+key down dpad_down
+key up dpad_down
+key down dpad_down
+key up dpad_down
+key down dpad_center
+key up dpad_center
+# account name: bill
+key down b
+key up b
+key down i
+key up i
+key down l
+key up l
+key down l
+key up l
+
+# Go down to the password field
+key down dpad_down
+key up dpad_down
+
+# password: bill
+key down b
+key up b
+key down i
+key up i
+key down l
+key up l
+key down l
+key up l
+
+# Select next
+touch down 300 450
+touch up 300 450
+
+# quit
+quit
index 7ebd727..521de16 100644 (file)
@@ -47,10 +47,10 @@ import java.util.List;
  * Application that injects random key events and other actions into the system.
  */
 public class Monkey {
-    
+
     /**
      * Monkey Debugging/Dev Support
-     * 
+     *
      * All values should be zero when checking in.
      */
     private final static int DEBUG_ALLOW_ANY_STARTS = 0;
@@ -74,20 +74,20 @@ public class Monkey {
 
     /** Ignore any not responding timeouts while running? */
     private boolean mIgnoreTimeouts;
-    
+
     /** Ignore security exceptions when launching activities */
     /** (The activity launch still fails, but we keep pluggin' away) */
     private boolean mIgnoreSecurityExceptions;
-    
+
     /** Monitor /data/tombstones and stop the monkey if new files appear. */
     private boolean mMonitorNativeCrashes;
-    
+
     /** Send no events.  Use with long throttle-time to watch user operations */
     private boolean mSendNoEvents;
 
     /** This is set when we would like to abort the running of the monkey. */
     private boolean mAbort;
-    
+
     /** This is set by the ActivityController thread to request collection of ANR trace files */
     private boolean mRequestAnrTraces = false;
 
@@ -96,7 +96,7 @@ public class Monkey {
 
     /** Kill the process after a timeout or crash. */
     private boolean mKillProcessAfterError;
-    
+
     /** Generate hprof reports before/after monkey runs */
     private boolean mGenerateHprof;
 
@@ -106,16 +106,16 @@ public class Monkey {
     ArrayList<String> mMainCategories = new ArrayList<String>();
     /** Applications we can switch to. */
     private ArrayList<ComponentName> mMainApps = new ArrayList<ComponentName>();
-    
+
     /** The delay between event inputs **/
     long mThrottle = 0;
-    
+
     /** The number of iterations **/
     int mCount = 1000;
-    
+
     /** The random number seed **/
     long mSeed = 0;
-    
+
     /** Dropped-event statistics **/
     long mDroppedKeyEvents = 0;
     long mDroppedPointerEvents = 0;
@@ -124,14 +124,21 @@ public class Monkey {
 
     /** a filename to the script (if any) **/
     private String mScriptFileName = null;
-    
+
+    /** a TCP port to listen on for remote commands. */
+    private int mServerPort = -1;
+
     private static final File TOMBSTONES_PATH = new File("/data/tombstones");
     private HashSet<String> mTombstones = null;
-    
-    float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];    
+
+    float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];
     MonkeyEventSource mEventSource;
     private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor();
-    
+
+    // information on the current activity.
+    public static Intent currentIntent;
+    public static String currentPackage;
+
     /**
      * Monitor operations happening in the system.
      */
@@ -142,21 +149,24 @@ public class Monkey {
                 System.out.println("    // " + (allow ? "Allowing" : "Rejecting")
                         + " start of " + intent + " in package " + pkg);
             }
+            currentPackage = pkg;
+            currentIntent = intent;
             return allow;
         }
-        
+
         public boolean activityResuming(String pkg) {
             System.out.println("    // activityResuming(" + pkg + ")");
             boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_RESTARTS != 0);
             if (!allow) {
                 if (mVerbose > 0) {
                     System.out.println("    // " + (allow ? "Allowing" : "Rejecting")
-                            + " resume of package " + pkg);
+                                       + " resume of package " + pkg);
                 }
             }
+            currentPackage = pkg;
             return allow;
         }
-        
+
         private boolean checkEnteringPackage(String pkg) {
             if (pkg == null) {
                 return true;
@@ -168,7 +178,7 @@ public class Monkey {
                 return mValidPackages.contains(pkg);
             }
         }
-        
+
         public boolean appCrashed(String processName, int pid, String shortMsg,
                 String longMsg, byte[] crashData) {
             System.err.println("// CRASH: " + processName + " (pid " + pid
@@ -223,14 +233,14 @@ public class Monkey {
             return 1;
         }
     }
-  
+
     /**
      * Run the procrank tool to insert system status information into the debug report.
      */
     private void reportProcRank() {
       commandLineReport("procrank", "procrank");
     }
-  
+
     /**
      * Run "cat /data/anr/traces.txt".  Wait about 5 seconds first, to let the asynchronous
      * report writing complete.
@@ -238,21 +248,21 @@ public class Monkey {
     private void reportAnrTraces() {
         try {
             Thread.sleep(5 * 1000);
-        } catch (InterruptedException e) { 
+        } catch (InterruptedException e) {
         }
         commandLineReport("anr traces", "cat /data/anr/traces.txt");
     }
-    
+
     /**
      * Run "dumpsys meminfo"
-     * 
+     *
      * NOTE:  You cannot perform a dumpsys call from the ActivityController callback, as it will
      * deadlock.  This should only be called from the main loop of the monkey.
      */
     private void reportDumpsysMemInfo() {
         commandLineReport("meminfo", "dumpsys meminfo");
     }
-    
+
     /**
      * Print report from a single command line.
      * @param reportName Simple tag that will print before the report and in various annotations.
@@ -266,7 +276,7 @@ public class Monkey {
         try {
             // Process must be fully qualified here because android.os.Process is used elsewhere
             java.lang.Process p = Runtime.getRuntime().exec(command);
-            
+
             // pipe everything from process stdout -> System.err
             InputStream inStream = p.getInputStream();
             InputStreamReader inReader = new InputStreamReader(inStream);
@@ -275,7 +285,7 @@ public class Monkey {
             while ((s = inBuffer.readLine()) != null) {
                 System.err.println(s);
             }
-            
+
             int status = p.waitFor();
             System.err.println("// " + reportName + " status was " + status);
         } catch (Exception e) {
@@ -307,26 +317,26 @@ public class Monkey {
                 Debug.waitForDebugger();
             }
         }
-        
+
         // Default values for some command-line options
         mVerbose = 0;
         mCount = 1000;
         mSeed = 0;
         mThrottle = 0;
-        
+
         // prepare for command-line processing
         mArgs = args;
         mNextArg = 0;
-        
+
         //set a positive value, indicating none of the factors is provided yet
         for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
             mFactors[i] = 1.0f;
         }
-        
+
         if (!processOptions()) {
             return -1;
         }
-        
+
         // now set up additional data in preparation for launch
         if (mMainCategories.size() == 0) {
             mMainCategories.add(Intent.CATEGORY_LAUNCHER);
@@ -348,11 +358,11 @@ public class Monkey {
                 }
             }
         }
-        
+
         if (!checkInternalConfiguration()) {
             return -2;
         }
-        
+
         if (!getSystemInterfaces()) {
             return -3;
         }
@@ -360,11 +370,19 @@ public class Monkey {
         if (!getMainApps()) {
             return -4;
         }
-        
+
         if (mScriptFileName != null) {
             // script mode, ignore other options
             mEventSource = new MonkeySourceScript(mScriptFileName, mThrottle);
             mEventSource.setVerbose(mVerbose);
+        } else if (mServerPort != -1) {
+            try {
+                mEventSource = new MonkeySourceNetwork(mServerPort);
+            } catch (IOException e) {
+                System.out.println("Error binding to network socket.");
+                return -5;
+            }
+            mCount = Integer.MAX_VALUE;
         } else {
             // random source by default
             if (mVerbose >= 2) {    // check seeding performance
@@ -378,7 +396,7 @@ public class Monkey {
                     ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
                 }
             }
-            
+
             //in random mode, we start with a random activity
             ((MonkeySourceRandom) mEventSource).generateActivity();
         }
@@ -387,7 +405,7 @@ public class Monkey {
         if (!mEventSource.validate()) {
             return -5;
         }
-        
+
         if (mScriptFileName != null) {
             // in random mode, count is the number of single events
             // while in script mode, count is the number of repetition
@@ -396,12 +414,12 @@ public class Monkey {
             mCount = mCount * ((MonkeySourceScript) mEventSource)
                 .getOneRoundEventCount();
         }
-        
+
         // If we're profiling, do it immediately before/after the main monkey loop
         if (mGenerateHprof) {
             signalPersistentProcesses();
         }
-        
+
         mNetworkMonitor.start();
         int crashedAtCycle = runMonkeyCycles();
         mNetworkMonitor.stop();
@@ -423,7 +441,7 @@ public class Monkey {
                 System.out.println("// Generated profiling reports in /data/misc");
             }
         }
-        
+
         try {
             mAm.setActivityController(null);
             mNetworkMonitor.unregister(mAm);
@@ -434,7 +452,7 @@ public class Monkey {
                 crashedAtCycle = mCount - 1;
             }
         }
-        
+
         // report dropped event stats
         if (mVerbose > 0) {
             System.out.print(":Dropped: keys=");
@@ -446,7 +464,7 @@ public class Monkey {
             System.out.print(" flips=");
             System.out.println(mDroppedFlipEvents);
         }
-        
+
         // report network stats
         mNetworkMonitor.dump();
 
@@ -461,10 +479,10 @@ public class Monkey {
             return 0;
         }
     }
-    
+
     /**
      * Process the command-line options
-     * 
+     *
      * @return Returns true if options were parsed with no apparent errors.
      */
     private boolean processOptions() {
@@ -498,28 +516,28 @@ public class Monkey {
                 } else if (opt.equals("--hprof")) {
                     mGenerateHprof = true;
                 } else if (opt.equals("--pct-touch")) {
-                    mFactors[MonkeySourceRandom.FACTOR_TOUCH] = 
+                    mFactors[MonkeySourceRandom.FACTOR_TOUCH] =
                         -nextOptionLong("touch events percentage");
                 } else if (opt.equals("--pct-motion")) {
-                    mFactors[MonkeySourceRandom.FACTOR_MOTION] = 
+                    mFactors[MonkeySourceRandom.FACTOR_MOTION] =
                         -nextOptionLong("motion events percentage");
                 } else if (opt.equals("--pct-trackball")) {
-                    mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] = 
+                    mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] =
                         -nextOptionLong("trackball events percentage");
                 } else if (opt.equals("--pct-nav")) {
-                    mFactors[MonkeySourceRandom.FACTOR_NAV] = 
+                    mFactors[MonkeySourceRandom.FACTOR_NAV] =
                         -nextOptionLong("nav events percentage");
                 } else if (opt.equals("--pct-majornav")) {
-                    mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] = 
+                    mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] =
                         -nextOptionLong("major nav events percentage");
                 } else if (opt.equals("--pct-appswitch")) {
-                    mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] = 
+                    mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] =
                         -nextOptionLong("app switch events percentage");
                 } else if (opt.equals("--pct-flip")) {
                     mFactors[MonkeySourceRandom.FACTOR_FLIP] =
                         -nextOptionLong("keyboard flip percentage");
                 } else if (opt.equals("--pct-anyevent")) {
-                    mFactors[MonkeySourceRandom.FACTOR_ANYTHING] = 
+                    mFactors[MonkeySourceRandom.FACTOR_ANYTHING] =
                         -nextOptionLong("any events percentage");
                 } else if (opt.equals("--throttle")) {
                     mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
@@ -527,7 +545,9 @@ public class Monkey {
                     // do nothing - it's caught at the very start of run()
                 } else if (opt.equals("--dbg-no-events")) {
                     mSendNoEvents = true;
-                } else  if (opt.equals("-f")) {
+                } else if (opt.equals("--port")) {
+                    mServerPort = (int) nextOptionLong("Server port to listen on for commands");
+                } else if (opt.equals("-f")) {
                     mScriptFileName = nextOptionData();
                 } else if (opt.equals("-h")) {
                     showUsage();
@@ -544,19 +564,23 @@ public class Monkey {
             return false;
         }
 
-        String countStr = nextArg();
-        if (countStr == null) {
-            System.err.println("** Error: Count not specified");
-            showUsage();
-            return false;
-        }
+        // If a server port hasn't been specified, we need to specify
+        // a count
+        if (mServerPort == -1) {
+            String countStr = nextArg();
+            if (countStr == null) {
+                System.err.println("** Error: Count not specified");
+                showUsage();
+                return false;
+            }
 
-        try {
-            mCount = Integer.parseInt(countStr);
-        } catch (NumberFormatException e) {
-            System.err.println("** Error: Count is not a number");
-            showUsage();
-            return false;
+            try {
+                mCount = Integer.parseInt(countStr);
+            } catch (NumberFormatException e) {
+                System.err.println("** Error: Count is not a number");
+                showUsage();
+                return false;
+            }
         }
 
         return true;
@@ -564,7 +588,7 @@ public class Monkey {
 
     /**
      * Check for any internal configuration (primarily build-time) errors.
-     * 
+     *
      * @return Returns true if ready to rock.
      */
     private boolean checkInternalConfiguration() {
@@ -585,7 +609,7 @@ public class Monkey {
 
     /**
      * Attach to the required system interfaces.
-     * 
+     *
      * @return Returns true if all system interfaces were available.
      */
     private boolean getSystemInterfaces() {
@@ -621,7 +645,7 @@ public class Monkey {
     /**
      * Using the restrictions provided (categories & packages), generate a list of activities
      * that we can actually switch to.
-     * 
+     *
      * @return Returns true if it could successfully build a list of target activities
      */
     private boolean getMainApps() {
@@ -644,7 +668,7 @@ public class Monkey {
                 final int NA = mainApps.size();
                 for (int a = 0; a < NA; a++) {
                     ResolveInfo r = mainApps.get(a);
-                    if (mValidPackages.size() == 0 || 
+                    if (mValidPackages.size() == 0 ||
                             mValidPackages.contains(r.activityInfo.applicationInfo.packageName)) {
                         if (mVerbose >= 2) {     // very verbose
                             System.out.println("//   + Using main activity "
@@ -676,15 +700,15 @@ public class Monkey {
             System.out.println("** No activities found to run, monkey aborted.");
             return false;
         }
-        
+
         return true;
     }
 
     /**
      * Run mCount cycles and see if we hit any crashers.
-     * 
+     *
      * TODO: Meta state on keys
-     * 
+     *
      * @return Returns the last cycle which executed. If the value == mCount, no errors detected.
      */
     private int runMonkeyCycles() {
@@ -749,9 +773,11 @@ public class Monkey {
                 } else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
                     systemCrashed = !mIgnoreSecurityExceptions;
                 }
+            } else {
+                // Event Source has signaled that we have no more events to process
+                break;
             }
         }
-
         // If we got this far, we succeeded!
         return mCount;
     }
@@ -775,18 +801,18 @@ public class Monkey {
 
     /**
      * Watch for appearance of new tombstone files, which indicate native crashes.
-     * 
+     *
      * @return Returns true if new files have appeared in the list
      */
     private boolean checkNativeCrashes() {
         String[] tombstones = TOMBSTONES_PATH.list();
-        
+
         // shortcut path for usually empty directory, so we don't waste even more objects
         if ((tombstones == null) || (tombstones.length == 0)) {
             mTombstones = null;
             return false;
         }
-        
+
         // use set logic to look for new files
         HashSet<String> newStones = new HashSet<String>();
         for (String x : tombstones) {
@@ -804,14 +830,14 @@ public class Monkey {
     /**
      * Return the next command line option.  This has a number of special cases which
      * closely, but not exactly, follow the POSIX command line options patterns:
-     *  
+     *
      * -- means to stop processing additional options
      * -z means option z
      * -z ARGS means option z with (non-optional) arguments ARGS
      * -zARGS means option z with (optional) arguments ARGS
      * --zz means option zz
      * --zz ARGS means option zz with (non-optional) arguments ARGS
-     * 
+     *
      * Note that you cannot combine single letter options;  -abc != -a -b -c
      *
      * @return Returns the option string, or null if there are no more options.
@@ -857,10 +883,10 @@ public class Monkey {
         mNextArg++;
         return data;
     }
-    
+
     /**
      * Returns a long converted from the next data argument, with error handling if not available.
-     * 
+     *
      * @param opt The name of the option.
      * @return Returns a long converted from the argument.
      */
@@ -904,6 +930,7 @@ public class Monkey {
       System.err.println("              [--pct-appswitch PERCENT] [--pct-flip PERCENT]");
       System.err.println("              [--pct-anyevent PERCENT]");
       System.err.println("              [--wait-dbg] [--dbg-no-events] [-f scriptfile]");
+      System.err.println("              [--port port]");
       System.err.println("              [-s SEED] [-v [-v] ...] [--throttle MILLISEC]");
       System.err.println("              COUNT");
   }
index d926be8..2783dde 100644 (file)
@@ -22,7 +22,7 @@ import android.view.IWindowManager;
 /**
  * abstract class for monkey event
  */
-public abstract class MonkeyEvent {    
+public abstract class MonkeyEvent {
     protected int eventType;
     public static final int EVENT_TYPE_KEY = 0;
     public static final int EVENT_TYPE_POINTER = 1;
@@ -30,41 +30,42 @@ public abstract class MonkeyEvent {
     public static final int EVENT_TYPE_ACTIVITY = 3;
     public static final int EVENT_TYPE_FLIP = 4; // Keyboard flip
     public static final int EVENT_TYPE_THROTTLE = 5;
-        
+    public static final int EVENT_TYPE_NOOP = 6;
+
     public static final int INJECT_SUCCESS = 1;
     public static final int INJECT_FAIL = 0;
 
     // error code for remote exception during injection
-    public static final int INJECT_ERROR_REMOTE_EXCEPTION = -1;    
+    public static final int INJECT_ERROR_REMOTE_EXCEPTION = -1;
     // error code for security exception during injection
     public static final int INJECT_ERROR_SECURITY_EXCEPTION = -2;
-        
+
     public MonkeyEvent(int type) {
         eventType = type;
     }
-  
-    /** 
+
+    /**
      * @return event type
-     */    
+     */
     public int getEventType() {
         return eventType;
     }
-    
+
     /**
      * @return true if it is safe to throttle after this event, and false otherwise.
      */
     public boolean isThrottlable() {
         return true;
     }
-    
-    
+
+
     /**
      * a method for injecting event
      * @param iwm wires to current window manager
      * @param iam wires to current activity manager
-     * @param verbose a log switch 
+     * @param verbose a log switch
      * @return INJECT_SUCCESS if it goes through, and INJECT_FAIL if it fails
      *         in the case of exceptions, return its corresponding error code
      */
-    public abstract int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose);    
+    public abstract int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose);
 }
index 877ebb5..d9c68af 100644 (file)
@@ -23,7 +23,7 @@ import android.view.KeyEvent;
 /**
  * monkey key event
  */
-public class MonkeyKeyEvent extends MonkeyEvent {    
+public class MonkeyKeyEvent extends MonkeyEvent {
     private long mDownTime = -1;
     private int mMetaState = -1;
     private int mAction = -1;
@@ -32,18 +32,25 @@ public class MonkeyKeyEvent extends MonkeyEvent {
     private int mRepeatCount = -1;
     private int mDeviceId = -1;
     private long mEventTime = -1;
-    
+
+    private KeyEvent keyEvent = null;
+
     public MonkeyKeyEvent(int action, int keycode) {
         super(EVENT_TYPE_KEY);
         mAction = action;
         mKeyCode = keycode;
     }
-    
+
+    public MonkeyKeyEvent(KeyEvent e) {
+        super(EVENT_TYPE_KEY);
+        keyEvent = e;
+    }
+
     public MonkeyKeyEvent(long downTime, long eventTime, int action,
             int code, int repeat, int metaState,
             int device, int scancode) {
         super(EVENT_TYPE_KEY);
-        
+
         mAction = action;
         mKeyCode = code;
         mMetaState = metaState;
@@ -53,49 +60,52 @@ public class MonkeyKeyEvent extends MonkeyEvent {
         mDownTime = downTime;
         mEventTime = eventTime;
     }
-    
+
     public int getKeyCode() {
         return mKeyCode;
     }
-    
+
     public int getAction() {
         return mAction;
     }
-    
+
     public long getDownTime() {
         return mDownTime;
     }
-    
+
     public long getEventTime() {
         return mEventTime;
     }
-    
+
     public void setDownTime(long downTime) {
         mDownTime = downTime;
     }
-    
+
     public void setEventTime(long eventTime) {
         mEventTime = eventTime;
     }
-    
-    /** 
+
+    /**
      * @return the key event
-     */        
+     */
     private KeyEvent getEvent() {
-        if (mDeviceId < 0) {
-            return new KeyEvent(mAction, mKeyCode);
-        }       
-        
-        // for scripts
-        return new KeyEvent(mDownTime, mEventTime, mAction, 
-                mKeyCode, mRepeatCount, mMetaState, mDeviceId, mScancode);
+        if (keyEvent == null) {
+            if (mDeviceId < 0) {
+                keyEvent = new KeyEvent(mAction, mKeyCode);
+            } else {
+                // for scripts
+                keyEvent = new KeyEvent(mDownTime, mEventTime, mAction,
+                                        mKeyCode, mRepeatCount, mMetaState, mDeviceId, mScancode);
+            }
+        }
+        return keyEvent;
     }
 
     @Override
     public boolean isThrottlable() {
         return (getAction() == KeyEvent.ACTION_UP);
     }
-    
+
     @Override
     public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
         if (verbose > 1) {
@@ -124,7 +134,7 @@ public class MonkeyKeyEvent extends MonkeyEvent {
         } catch (RemoteException ex) {
             return MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION;
         }
-        
+
         return MonkeyEvent.INJECT_SUCCESS;
     }
 }
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyNoopEvent.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyNoopEvent.java
new file mode 100644 (file)
index 0000000..ea92735
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.monkey;
+
+import java.util.List;
+
+import android.app.IActivityManager;
+import android.view.IWindowManager;
+
+
+/**
+ * monkey noop event (don't do anything).
+ */
+public class MonkeyNoopEvent extends MonkeyEvent {
+
+    public MonkeyNoopEvent() {
+        super(MonkeyEvent.EVENT_TYPE_NOOP);
+    }
+
+    @Override
+    public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
+        // No real work to do
+        if (verbose > 1) {
+            System.out.println("NOOP");
+        }
+        return MonkeyEvent.INJECT_SUCCESS;
+    }
+}
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetwork.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetwork.java
new file mode 100644 (file)
index 0000000..a9a1db4
--- /dev/null
@@ -0,0 +1,697 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.commands.monkey;
+
+import android.content.Context;
+import android.os.IPowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.lang.Integer;
+import java.lang.NumberFormatException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.StringTokenizer;
+
+/**
+ * An Event source for getting Monkey Network Script commands from
+ * over the network.
+ */
+public class MonkeySourceNetwork implements MonkeyEventSource {
+    private static final String TAG = "MonkeyStub";
+
+    /**
+     * ReturnValue from the MonkeyCommand that indicates whether the
+     * command was sucessful or not.
+     */
+    public static class MonkeyCommandReturn {
+        private final boolean success;
+        private final String message;
+
+        public MonkeyCommandReturn(boolean success) {
+            this.success = success;
+            this.message = null;
+        }
+
+        public MonkeyCommandReturn(boolean success,
+                                   String message) {
+            this.success = success;
+            this.message = message;
+        }
+
+        boolean hasMessage() {
+            return message != null;
+        }
+
+        String getMessage() {
+            return message;
+        }
+
+        boolean wasSuccessful() {
+            return success;
+        }
+    }
+
+    public final static MonkeyCommandReturn OK = new MonkeyCommandReturn(true);
+    public final static MonkeyCommandReturn ERROR = new MonkeyCommandReturn(false);
+    public final static MonkeyCommandReturn EARG = new MonkeyCommandReturn(false,
+                                                                            "Invalid Argument");
+
+    /**
+     * Interface that MonkeyCommands must implement.
+     */
+    public interface MonkeyCommand {
+        /**
+         * Translate the command line into a sequence of MonkeyEvents.
+         *
+         * @param command the command line.
+         * @param queue the command queue.
+         * @returs MonkeyCommandReturn indicating what happened.
+         */
+        MonkeyCommandReturn translateCommand(List<String> command, CommandQueue queue);
+    }
+
+    /**
+     * Command to simulate closing and opening the keyboard.
+     */
+    private static class FlipCommand implements MonkeyCommand {
+        // flip open
+        // flip closed
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() > 1) {
+                String direction = command.get(1);
+                if ("open".equals(direction)) {
+                    queue.enqueueEvent(new MonkeyFlipEvent(true));
+                    return OK;
+                } else if ("close".equals(direction)) {
+                    queue.enqueueEvent(new MonkeyFlipEvent(false));
+                    return OK;
+                }
+            }
+            return EARG;
+        }
+    }
+
+    /**
+     * Command to send touch events to the input system.
+     */
+    private static class TouchCommand implements MonkeyCommand {
+        // touch [down|up|move] [x] [y]
+        // touch down 120 120
+        // touch move 140 140
+        // touch up 140 140
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() == 4) {
+                String actionName = command.get(1);
+                int x = 0;
+                int y = 0;
+                try {
+                    x = Integer.parseInt(command.get(2));
+                    y = Integer.parseInt(command.get(3));
+                } catch (NumberFormatException e) {
+                    // Ok, it wasn't a number
+                    Log.e(TAG, "Got something that wasn't a number", e);
+                    return EARG;
+                }
+
+                // figure out the action
+                int action = -1;
+                if ("down".equals(actionName)) {
+                    action = MotionEvent.ACTION_DOWN;
+                } else if ("up".equals(actionName)) {
+                    action = MotionEvent.ACTION_UP;
+                } else if ("move".equals(actionName)) {
+                    action = MotionEvent.ACTION_MOVE;
+                }
+                if (action == -1) {
+                    Log.e(TAG, "Got a bad action: " + actionName);
+                    return EARG;
+                }
+
+                queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+                                                         -1, action, x, y, 0));
+                return OK;
+            }
+            return EARG;
+        }
+    }
+
+    /**
+     * Command to send Trackball events to the input system.
+     */
+    private static class TrackballCommand implements MonkeyCommand {
+        // trackball [dx] [dy]
+        // trackball 1 0 -- move right
+        // trackball -1 0 -- move left
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() == 3) {
+                int dx = 0;
+                int dy = 0;
+                try {
+                    dx = Integer.parseInt(command.get(1));
+                    dy = Integer.parseInt(command.get(2));
+                } catch (NumberFormatException e) {
+                    // Ok, it wasn't a number
+                    Log.e(TAG, "Got something that wasn't a number", e);
+                    return EARG;
+                }
+                queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
+                                                         MotionEvent.ACTION_MOVE, dx, dy, 0));
+                return OK;
+
+            }
+            return EARG;
+        }
+    }
+
+    /**
+     * Command to send Key events to the input system.
+     */
+    private static class KeyCommand implements MonkeyCommand {
+        // key [down|up] [keycode]
+        // key down 82
+        // key up 82
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() == 3) {
+                int keyCode = getKeyCode(command.get(2));
+                if (keyCode < 0) {
+                    // Ok, you gave us something bad.
+                    Log.e(TAG, "Can't find keyname: " + command.get(2));
+                    return EARG;
+                }
+                Log.d(TAG, "keycode: " + keyCode);
+                int action = -1;
+                if ("down".equals(command.get(1))) {
+                    action = KeyEvent.ACTION_DOWN;
+                } else if ("up".equals(command.get(1))) {
+                    action = KeyEvent.ACTION_UP;
+                }
+                if (action == -1) {
+                    Log.e(TAG, "got unknown action.");
+                    return EARG;
+                }
+                queue.enqueueEvent(new MonkeyKeyEvent(action, keyCode));
+                return OK;
+            }
+            return EARG;
+        }
+    }
+
+    /**
+     * Get an integer keycode value from a given keyname.
+     *
+     * @param keyName the key name to get the code for
+     * @returns the integer keycode value, or -1 on error.
+     */
+    private static int getKeyCode(String keyName) {
+        int keyCode = -1;
+        try {
+            keyCode = Integer.parseInt(keyName);
+        } catch (NumberFormatException e) {
+            // Ok, it wasn't a number, see if we have a
+            // keycode name for it
+            keyCode = MonkeySourceRandom.getKeyCode(keyName);
+            if (keyCode == -1) {
+                // OK, one last ditch effort to find a match.
+                // Build the KEYCODE_STRING from the string
+                // we've been given and see if that key
+                // exists.  This would allow you to do "key
+                // down menu", for example.
+                keyCode = MonkeySourceRandom.getKeyCode("KEYCODE_" + keyName.toUpperCase());
+            }
+        }
+        return keyCode;
+    }
+
+    /**
+     * Command to put the Monkey to sleep.
+     */
+    private static class SleepCommand implements MonkeyCommand {
+        // sleep 2000
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() == 2) {
+                int sleep = -1;
+                String sleepStr = command.get(1);
+                try {
+                    sleep = Integer.parseInt(sleepStr);
+                } catch (NumberFormatException e) {
+                    Log.e(TAG, "Not a number: " + sleepStr, e);
+                    return EARG;
+                }
+                queue.enqueueEvent(new MonkeyThrottleEvent(sleep));
+                return OK;
+            }
+            return EARG;
+        }
+    }
+
+    /**
+     * Command to type a string
+     */
+    private static class TypeCommand implements MonkeyCommand {
+        // wake
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() == 2) {
+                String str = command.get(1);
+
+                char[] chars = str.toString().toCharArray();
+
+                // Convert the string to an array of KeyEvent's for
+                // the built in keymap.
+                KeyCharacterMap keyCharacterMap = KeyCharacterMap.
+                        load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+                KeyEvent[] events = keyCharacterMap.getEvents(chars);
+
+                // enqueue all the events we just got.
+                for (KeyEvent event : events) {
+                    queue.enqueueEvent(new MonkeyKeyEvent(event));
+                }
+                return OK;
+            }
+            return EARG;
+        }
+    }
+
+    /**
+     * Command to wake the device up
+     */
+    private static class WakeCommand implements MonkeyCommand {
+        // wake
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (!wake()) {
+                return ERROR;
+            }
+            return OK;
+        }
+    }
+
+    /**
+     * Command to "tap" at a location (Sends a down and up touch
+     * event).
+     */
+    private static class TapCommand implements MonkeyCommand {
+        // tap x y
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() == 3) {
+                int x = 0;
+                int y = 0;
+                try {
+                    x = Integer.parseInt(command.get(1));
+                    y = Integer.parseInt(command.get(2));
+                } catch (NumberFormatException e) {
+                    // Ok, it wasn't a number
+                    Log.e(TAG, "Got something that wasn't a number", e);
+                    return EARG;
+                }
+
+                queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+                                                         -1, MotionEvent.ACTION_DOWN,
+                                                         x, y, 0));
+                queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+                                                         -1, MotionEvent.ACTION_UP,
+                                                         x, y, 0));
+                return OK;
+            }
+            return EARG;
+        }
+    }
+
+    /**
+     * Command to "press" a buttons (Sends an up and down key event.)
+     */
+    private static class PressCommand implements MonkeyCommand {
+        // press keycode
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() == 2) {
+                int keyCode = getKeyCode(command.get(1));
+                if (keyCode < 0) {
+                    // Ok, you gave us something bad.
+                    Log.e(TAG, "Can't find keyname: " + command.get(1));
+                    return EARG;
+                }
+
+                queue.enqueueEvent(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+                queue.enqueueEvent(new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode));
+                return OK;
+
+            }
+            return EARG;
+        }
+    }
+
+    /**
+     * Force the device to wake up.
+     *
+     * @return true if woken up OK.
+     */
+    private static final boolean wake() {
+        IPowerManager pm =
+                IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
+        try {
+            pm.userActivityWithForce(SystemClock.uptimeMillis(), true, true);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Got remote exception", e);
+            return false;
+        }
+        return true;
+    }
+
+    // This maps from command names to command implementations.
+    private static final Map<String, MonkeyCommand> COMMAND_MAP = new HashMap<String, MonkeyCommand>();
+
+    static {
+        // Add in all the commands we support
+        COMMAND_MAP.put("flip", new FlipCommand());
+        COMMAND_MAP.put("touch", new TouchCommand());
+        COMMAND_MAP.put("trackball", new TrackballCommand());
+        COMMAND_MAP.put("key", new KeyCommand());
+        COMMAND_MAP.put("sleep", new SleepCommand());
+        COMMAND_MAP.put("wake", new WakeCommand());
+        COMMAND_MAP.put("tap", new TapCommand());
+        COMMAND_MAP.put("press", new PressCommand());
+        COMMAND_MAP.put("type", new TypeCommand());
+        COMMAND_MAP.put("listvar", new MonkeySourceNetworkVars.ListVarCommand());
+        COMMAND_MAP.put("getvar", new MonkeySourceNetworkVars.GetVarCommand());
+    }
+
+    // QUIT command
+    private static final String QUIT = "quit";
+    // DONE command
+    private static final String DONE = "done";
+
+    // command response strings
+    private static final String OK_STR = "OK";
+    private static final String ERROR_STR = "ERROR";
+
+    public static interface CommandQueue {
+        /**
+         * Enqueue an event to be returned later.  This allows a
+         * command to return multiple events.  Commands using the
+         * command queue still have to return a valid event from their
+         * translateCommand method.  The returned command will be
+         * executed before anything put into the queue.
+         *
+         * @param e the event to be enqueued.
+         */
+        public void enqueueEvent(MonkeyEvent e);
+    };
+
+    // Queue of Events to be processed.  This allows commands to push
+    // multiple events into the queue to be processed.
+    private static class CommandQueueImpl implements CommandQueue{
+        private final Queue<MonkeyEvent> queuedEvents = new LinkedList<MonkeyEvent>();
+
+        public void enqueueEvent(MonkeyEvent e) {
+            queuedEvents.offer(e);
+        }
+
+        /**
+         * Get the next queued event to excecute.
+         *
+         * @returns the next event, or null if there aren't any more.
+         */
+        public MonkeyEvent getNextQueuedEvent() {
+            return queuedEvents.poll();
+        }
+    };
+
+    private final CommandQueueImpl commandQueue = new CommandQueueImpl();
+
+    private BufferedReader input;
+    private PrintWriter output;
+    private boolean started = false;
+
+    private ServerSocket serverSocket;
+    private Socket clientSocket;
+
+    public MonkeySourceNetwork(int port) throws IOException {
+        // Only bind this to local host.  This means that you can only
+        // talk to the monkey locally, or though adb port forwarding.
+        serverSocket = new ServerSocket(port,
+                                        0, // default backlog
+                                        InetAddress.getLocalHost());
+    }
+
+    /**
+     * Start a network server listening on the specified port.  The
+     * network protocol is a line oriented protocol, where each line
+     * is a different command that can be run.
+     *
+     * @param port the port to listen on
+     */
+    private void startServer() throws IOException {
+        clientSocket = serverSocket.accept();
+        // At this point, we have a client connected.  Wake the device
+        // up in preparation for doing some commands.
+        wake();
+
+        input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+        // auto-flush
+        output = new PrintWriter(clientSocket.getOutputStream(), true);
+    }
+
+    /**
+     * Stop the server from running so it can reconnect a new client.
+     */
+    private void stopServer() throws IOException {
+        clientSocket.close();
+        input.close();
+        output.close();
+        started = false;
+    }
+
+    /**
+     * Helper function for commandLineSplit that replaces quoted
+     * charaters with their real values.
+     *
+     * @param input the string to do replacement on.
+     * @returns the results with the characters replaced.
+     */
+    private static String replaceQuotedChars(String input) {
+        return input.replace("\\\"", "\"");
+    }
+
+    /**
+     * This function splits the given line into String parts.  It obey's quoted
+     * strings and returns them as a single part.
+     *
+     * "This is a test" -> returns only one element
+     * This is a test -> returns four elements
+     *
+     * @param line the line to parse
+     * @return the List of elements
+     */
+    private static List<String> commandLineSplit(String line) {
+        ArrayList<String> result = new ArrayList<String>();
+        StringTokenizer tok = new StringTokenizer(line);
+
+        boolean insideQuote = false;
+        StringBuffer quotedWord = new StringBuffer();
+        while (tok.hasMoreTokens()) {
+            String cur = tok.nextToken();
+            if (!insideQuote && cur.startsWith("\"")) {
+                // begin quote
+                quotedWord.append(replaceQuotedChars(cur));
+                insideQuote = true;
+            } else if (insideQuote) {
+                // end quote
+                if (cur.endsWith("\"")) {
+                    insideQuote = false;
+                    quotedWord.append(" ").append(replaceQuotedChars(cur));
+                    String word = quotedWord.toString();
+
+                    // trim off the quotes
+                    result.add(word.substring(1, word.length() - 1));
+                } else {
+                    quotedWord.append(" ").append(replaceQuotedChars(cur));
+                }
+            } else {
+                result.add(replaceQuotedChars(cur));
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Translate the given command line into a MonkeyEvent.
+     *
+     * @param commandLine the full command line given.
+     */
+    private void translateCommand(String commandLine) {
+        Log.d(TAG, "translateCommand: " + commandLine);
+        List<String> parts = commandLineSplit(commandLine);
+        if (parts.size() > 0) {
+            MonkeyCommand command = COMMAND_MAP.get(parts.get(0));
+            if (command != null) {
+                MonkeyCommandReturn ret = command.translateCommand(parts,
+                                                                   commandQueue);
+                if (ret.wasSuccessful()) {
+                    if (ret.hasMessage()) {
+                        returnOk(ret.getMessage());
+                    } else {
+                        returnOk();
+                    }
+                } else {
+                    if (ret.hasMessage()) {
+                        returnError(ret.getMessage());
+                    } else {
+                        returnError();
+                    }
+                }
+            }
+        }
+    }
+
+    public MonkeyEvent getNextEvent() {
+        if (!started) {
+            try {
+                startServer();
+            } catch (IOException e) {
+                Log.e(TAG, "Got IOException from server", e);
+                return null;
+            }
+            started = true;
+        }
+
+        // Now, get the next command.  This call may block, but that's OK
+        try {
+            while (true) {
+                // Check to see if we have any events queued up.  If
+                // we do, use those until we have no more.  Then get
+                // more input from the user.
+                MonkeyEvent queuedEvent = commandQueue.getNextQueuedEvent();
+                if (queuedEvent != null) {
+                    // dispatch the event
+                    return queuedEvent;
+                }
+
+                String command = input.readLine();
+                if (command == null) {
+                    Log.d(TAG, "Connection dropped.");
+                    // Treat this exactly the same as if the user had
+                    // ended the session cleanly with a done commant.
+                    command = DONE;
+                }
+
+                if (DONE.equals(command)) {
+                    // stop the server so it can accept new connections
+                    try {
+                        stopServer();
+                    } catch (IOException e) {
+                        Log.e(TAG, "Got IOException shutting down!", e);
+                        return null;
+                    }
+                    // return a noop event so we keep executing the main
+                    // loop
+                    return new MonkeyNoopEvent();
+                }
+
+                // Do quit checking here
+                if (QUIT.equals(command)) {
+                    // then we're done
+                    Log.d(TAG, "Quit requested");
+                    // let the host know the command ran OK
+                    returnOk();
+                    return null;
+                }
+
+                // Do comment checking here.  Comments aren't a
+                // command, so we don't echo anything back to the
+                // user.
+                if (command.startsWith("#")) {
+                    // keep going
+                    continue;
+                }
+
+                // Translate the command line.  This will handle returning error/ok to the user
+                translateCommand(command);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Exception: ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns ERROR to the user.
+     */
+    private void returnError() {
+        output.println(ERROR_STR);
+    }
+
+    /**
+     * Returns ERROR to the user.
+     *
+     * @param msg the error message to include
+     */
+    private void returnError(String msg) {
+        output.print(ERROR_STR);
+        output.print(":");
+        output.println(msg);
+    }
+
+    /**
+     * Returns OK to the user.
+     */
+    private void returnOk() {
+        output.println(OK_STR);
+    }
+
+    /**
+     * Returns OK to the user.
+     *
+     * @param returnValue the value to return from this command.
+     */
+    private void returnOk(String returnValue) {
+        output.print(OK_STR);
+        output.print(":");
+        output.println(returnValue);
+    }
+
+    public void setVerbose(int verbose) {
+        // We're not particualy verbose
+    }
+
+    public boolean validate() {
+        // we have no pre-conditions to validate
+        return true;
+    }
+}
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkVars.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkVars.java
new file mode 100644 (file)
index 0000000..e9890ad
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.commands.monkey;
+
+import android.os.Build;
+import android.os.SystemClock;
+import android.view.Display;
+import android.view.WindowManagerImpl;
+import android.util.DisplayMetrics;
+
+import com.android.commands.monkey.MonkeySourceNetwork.CommandQueue;
+import com.android.commands.monkey.MonkeySourceNetwork.MonkeyCommand;
+import com.android.commands.monkey.MonkeySourceNetwork.MonkeyCommandReturn;
+
+import java.lang.Integer;
+import java.lang.Float;
+import java.lang.Long;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class MonkeySourceNetworkVars {
+    /**
+     * Interface to get the value of a var.
+     */
+    private static interface VarGetter {
+        /**
+         * Get the value of the var.
+         * @returns the value of the var.
+         */
+        public String get();
+    }
+
+    private static class StaticVarGetter implements VarGetter {
+        private final String value;
+
+        public StaticVarGetter(String value) {
+            this.value = value;
+        }
+
+        public String get() {
+            return value;
+        }
+    }
+
+    // Use a TreeMap to keep the keys sorted so they get displayed nicely in listvar
+    private static final Map<String, VarGetter> VAR_MAP = new TreeMap<String, VarGetter>();
+
+    static {
+        VAR_MAP.put("build.board", new StaticVarGetter(Build.BOARD));
+        VAR_MAP.put("build.brand", new StaticVarGetter(Build.BRAND));
+        VAR_MAP.put("build.device", new StaticVarGetter(Build.DEVICE));
+        VAR_MAP.put("build.display", new StaticVarGetter(Build.DISPLAY));
+        VAR_MAP.put("build.fingerprint", new StaticVarGetter(Build.FINGERPRINT));
+        VAR_MAP.put("build.host", new StaticVarGetter(Build.HOST));
+        VAR_MAP.put("build.id", new StaticVarGetter(Build.ID));
+        VAR_MAP.put("build.model", new StaticVarGetter(Build.MODEL));
+        VAR_MAP.put("build.product", new StaticVarGetter(Build.PRODUCT));
+        VAR_MAP.put("build.tags", new StaticVarGetter(Build.TAGS));
+        VAR_MAP.put("build.brand", new StaticVarGetter(Long.toString(Build.TIME)));
+        VAR_MAP.put("build.type", new StaticVarGetter(Build.TYPE));
+        VAR_MAP.put("build.user", new StaticVarGetter(Build.USER));
+        VAR_MAP.put("build.cpu_abi", new StaticVarGetter(Build.CPU_ABI));
+        VAR_MAP.put("build.manufacturer", new StaticVarGetter(Build.MANUFACTURER));
+        VAR_MAP.put("build.version.incremental", new StaticVarGetter(Build.VERSION.INCREMENTAL));
+        VAR_MAP.put("build.version.release", new StaticVarGetter(Build.VERSION.RELEASE));
+        VAR_MAP.put("build.version.sdk", new StaticVarGetter(Integer.toString(Build.VERSION.SDK_INT)));
+        VAR_MAP.put("build.version.codename", new StaticVarGetter(Build.VERSION.CODENAME));
+
+        // Display
+        Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
+        VAR_MAP.put("display.width", new StaticVarGetter(Integer.toString(display.getWidth())));
+        VAR_MAP.put("display.height", new StaticVarGetter(Integer.toString(display.getHeight())));
+
+        DisplayMetrics dm = new DisplayMetrics();
+        display.getMetrics(dm);
+        VAR_MAP.put("display.density", new StaticVarGetter(Float.toString(dm.density)));
+
+        // am.  note that the current activity information isn't valid
+        // until the first activity gets launched after the monkey has
+        // been started.
+        VAR_MAP.put("am.current.package", new VarGetter() {
+                public String get() {
+                    return Monkey.currentPackage;
+                }
+            });
+        VAR_MAP.put("am.current.action", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    return Monkey.currentIntent.getAction();
+                }
+            });
+        VAR_MAP.put("am.current.comp.class", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    return Monkey.currentIntent.getComponent().getClassName();
+                }
+            });
+        VAR_MAP.put("am.current.comp.package", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    return Monkey.currentIntent.getComponent().getPackageName();
+                }
+            });
+        VAR_MAP.put("am.current.data", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    return Monkey.currentIntent.getDataString();
+                }
+            });
+        VAR_MAP.put("am.current.categories", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    StringBuffer sb = new StringBuffer();
+                    for (String cat : Monkey.currentIntent.getCategories()) {
+                        sb.append(cat).append(" ");
+                    }
+                    return sb.toString();
+                }
+            });
+
+        // clock
+        VAR_MAP.put("clock.realtime", new VarGetter() {
+                public String get() {
+                    return Long.toString(SystemClock.elapsedRealtime());
+                }
+            });
+        VAR_MAP.put("clock.uptime", new VarGetter() {
+                public String get() {
+                    return Long.toString(SystemClock.uptimeMillis());
+                }
+            });
+        VAR_MAP.put("clock.millis", new VarGetter() {
+                public String get() {
+                    return Long.toString(System.currentTimeMillis());
+                }
+            });
+    }
+
+    /**
+     * Command to list the "vars" that the monkey knows about.
+     */
+    public static class ListVarCommand implements MonkeySourceNetwork.MonkeyCommand {
+        // listvar
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            Set<String> keys = VAR_MAP.keySet();
+            StringBuffer sb = new StringBuffer();
+            for (String key : keys) {
+                sb.append(key).append(" ");
+            }
+            return new MonkeyCommandReturn(true, sb.toString());
+        }
+    }
+
+    /**
+     * Command to get the value of a var.
+     */
+    public static class GetVarCommand implements MonkeyCommand {
+        // getvar varname
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() == 2) {
+                VarGetter getter = VAR_MAP.get(command.get(1));
+                if (getter == null) {
+                    return new MonkeyCommandReturn(false, "unknown var");
+                }
+                return new MonkeyCommandReturn(true, getter.get());
+            }
+            return MonkeySourceNetwork.EARG;
+        }
+    }
+}
index 5f9c10f..27c8a51 100644 (file)
@@ -31,7 +31,7 @@ import java.util.Random;
 /**
  * monkey event queue
  */
-public class MonkeySourceRandom implements MonkeyEventSource {    
+public class MonkeySourceRandom implements MonkeyEventSource {
     /** Key events that move around the UI. */
     private static final int[] NAV_KEYS = {
         KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
@@ -55,7 +55,7 @@ public class MonkeySourceRandom implements MonkeyEventSource {
     /** Nice names for all key events. */
     private static final String[] KEY_NAMES = {
         "KEYCODE_UNKNOWN",
-        "KEYCODE_MENU",
+        "KEYCODE_SOFT_LEFT",
         "KEYCODE_SOFT_RIGHT",
         "KEYCODE_HOME",
         "KEYCODE_BACK",
@@ -146,7 +146,7 @@ public class MonkeySourceRandom implements MonkeyEventSource {
         "KEYCODE_REWIND",
         "KEYCODE_FORWARD",
         "KEYCODE_MUTE",
-        
+
         "TAG_LAST_KEYCODE"      // EOL.  used to keep the lists in sync
     };
 
@@ -158,34 +158,50 @@ public class MonkeySourceRandom implements MonkeyEventSource {
     public static final int FACTOR_SYSOPS       = 5;
     public static final int FACTOR_APPSWITCH    = 6;
     public static final int FACTOR_FLIP         = 7;
-    public static final int FACTOR_ANYTHING     = 8;    
+    public static final int FACTOR_ANYTHING     = 8;
     public static final int FACTORZ_COUNT       = 9;    // should be last+1
-    
-    
+
+
     /** percentages for each type of event.  These will be remapped to working
      * values after we read any optional values.
-     **/    
+     **/
     private float[] mFactors = new float[FACTORZ_COUNT];
     private ArrayList<ComponentName> mMainApps;
     private int mEventCount = 0;  //total number of events generated so far
     private MonkeyEventQueue mQ;
-    private Random mRandom;    
+    private Random mRandom;
     private int mVerbose = 0;
     private long mThrottle = 0;
 
     private boolean mKeyboardOpen = false;
 
-    /** 
+    /**
      * @return the last name in the key list
      */
     public static String getLastKeyName() {
         return KEY_NAMES[KeyEvent.getMaxKeyCode() + 1];
     }
-    
+
     public static String getKeyName(int keycode) {
         return KEY_NAMES[keycode];
     }
-    
+
+    /**
+     * Looks up the keyCode from a given KEYCODE_NAME.  NOTE: This may
+     * be an expensive operation.
+     *
+     * @param keyName the name of the KEYCODE_VALUE to lookup.
+     * @returns the intenger keyCode value, or -1 if not found
+     */
+    public static int getKeyCode(String keyName) {
+        for (int x = 0; x < KEY_NAMES.length; x++) {
+            if (KEY_NAMES[x].equals(keyName)) {
+                return x;
+            }
+        }
+        return -1;
+    }
+
     public MonkeySourceRandom(long seed, ArrayList<ComponentName> MainApps, long throttle) {
         // default values for random distributions
         // note, these are straight percentages, to match user input (cmd line args)
@@ -199,7 +215,7 @@ public class MonkeySourceRandom implements MonkeyEventSource {
         mFactors[FACTOR_APPSWITCH] = 2.0f;
         mFactors[FACTOR_FLIP] = 1.0f;
         mFactors[FACTOR_ANYTHING] = 15.0f;
-        
+
         mRandom = new SecureRandom();
         mRandom.setSeed((seed == 0) ? -1 : seed);
         mMainApps = MainApps;
@@ -220,25 +236,25 @@ public class MonkeySourceRandom implements MonkeyEventSource {
             } else {
                 defaultSum += mFactors[i];
                 ++defaultCount;
-            }            
+            }
         }
-        
+
         // if the user request was > 100%, reject it
         if (userSum > 100.0f) {
             System.err.println("** Event weights > 100%");
             return false;
         }
-        
+
         // if the user specified all of the weights, then they need to be 100%
         if (defaultCount == 0 && (userSum < 99.9f || userSum > 100.1f)) {
             System.err.println("** Event weights != 100%");
             return false;
         }
-        
+
         // compute the adjustment necessary
         float defaultsTarget = (100.0f - userSum);
         float defaultsAdjustment = defaultsTarget / defaultSum;
-        
+
         // fix all values, by adjusting defaults, or flipping user values back to >0
         for (int i = 0; i < FACTORZ_COUNT; ++i) {
             if (mFactors[i] <= 0.0f) {   // user values are zero or negative
@@ -247,46 +263,46 @@ public class MonkeySourceRandom implements MonkeyEventSource {
                 mFactors[i] *= defaultsAdjustment;
             }
         }
-        
+
         // if verbose, show factors
-        
+
         if (mVerbose > 0) {
             System.out.println("// Event percentages:");
             for (int i = 0; i < FACTORZ_COUNT; ++i) {
                 System.out.println("//   " + i + ": " + mFactors[i] + "%");
             }
-        } 
-        
+        }
+
         // finally, normalize and convert to running sum
         float sum = 0.0f;
         for (int i = 0; i < FACTORZ_COUNT; ++i) {
             sum += mFactors[i] / 100.0f;
             mFactors[i] = sum;
-        }        
+        }
         return true;
     }
-   
+
     /**
      * set the factors
-     * 
+     *
      * @param factors: percentages for each type of event
      */
     public void setFactors(float factors[]) {
         int c = FACTORZ_COUNT;
         if (factors.length < c) {
             c = factors.length;
-        }        
+        }
         for (int i = 0; i < c; i++)
             mFactors[i] = factors[i];
     }
-    
+
     public void setFactors(int index, float v) {
         mFactors[index] = v;
     }
-    
+
     /**
      * Generates a random motion event. This method counts a down, move, and up as multiple events.
-     * 
+     *
      * TODO:  Test & fix the selectors when non-zero percentages
      * TODO:  Longpress.
      * TODO:  Fling.
@@ -294,13 +310,13 @@ public class MonkeySourceRandom implements MonkeyEventSource {
      * TODO:  More useful than the random walk here would be to pick a single random direction
      * and distance, and divvy it up into a random number of segments.  (This would serve to
      * generate fling gestures, which are important).
-     * 
+     *
      * @param random Random number source for positioning
-     * @param motionEvent If false, touch/release.  If true, touch/move/release. 
-     * 
+     * @param motionEvent If false, touch/release.  If true, touch/move/release.
+     *
      */
     private void generateMotionEvent(Random random, boolean motionEvent){
-        
+
         Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
 
         float x = Math.abs(random.nextInt() % display.getWidth());
@@ -310,12 +326,12 @@ public class MonkeySourceRandom implements MonkeyEventSource {
         if (downAt == -1) {
             downAt = eventTime;
         }
-        
-        MonkeyMotionEvent e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER, 
-                downAt, MotionEvent.ACTION_DOWN, x, y, 0);        
-        e.setIntermediateNote(false);        
+
+        MonkeyMotionEvent e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+                downAt, MotionEvent.ACTION_DOWN, x, y, 0);
+        e.setIntermediateNote(false);
         mQ.addLast(e);
-        
+
         // sometimes we'll move during the touch
         if (motionEvent) {
             int count = random.nextInt(10);
@@ -323,34 +339,34 @@ public class MonkeySourceRandom implements MonkeyEventSource {
                 // generate some slop in the up event
                 x = (x + (random.nextInt() % 10)) % display.getWidth();
                 y = (y + (random.nextInt() % 10)) % display.getHeight();
-                
-                e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER, 
-                        downAt, MotionEvent.ACTION_MOVE, x, y, 0);        
-                e.setIntermediateNote(true);        
+
+                e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+                        downAt, MotionEvent.ACTION_MOVE, x, y, 0);
+                e.setIntermediateNote(true);
                 mQ.addLast(e);
             }
         }
 
         // TODO generate some slop in the up event
-        e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER, 
-                downAt, MotionEvent.ACTION_UP, x, y, 0);        
-        e.setIntermediateNote(false);        
+        e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+                downAt, MotionEvent.ACTION_UP, x, y, 0);
+        e.setIntermediateNote(false);
         mQ.addLast(e);
     }
-  
+
     /**
      * Generates a random trackball event. This consists of a sequence of small moves, followed by
      * an optional single click.
-     * 
+     *
      * TODO:  Longpress.
      * TODO:  Meta state
      * TODO:  Parameterize the % clicked
      * TODO:  More useful than the random walk here would be to pick a single random direction
      * and distance, and divvy it up into a random number of segments.  (This would serve to
      * generate fling gestures, which are important).
-     * 
+     *
      * @param random Random number source for positioning
-     * 
+     *
      */
     private void generateTrackballEvent(Random random) {
         Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
@@ -362,47 +378,47 @@ public class MonkeySourceRandom implements MonkeyEventSource {
             // generate a small random step
             int dX = random.nextInt(10) - 5;
             int dY = random.nextInt(10) - 5;
-            
-            
-            e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1, 
-                    MotionEvent.ACTION_MOVE, dX, dY, 0);        
-            e.setIntermediateNote(i > 0);        
+
+
+            e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
+                    MotionEvent.ACTION_MOVE, dX, dY, 0);
+            e.setIntermediateNote(i > 0);
             mQ.addLast(e);
         }
-        
+
         // 10% of trackball moves end with a click
         if (0 == random.nextInt(10)) {
             long downAt = SystemClock.uptimeMillis();
-            
-            
-            e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt, 
-                    MotionEvent.ACTION_DOWN, 0, 0, 0);        
-            e.setIntermediateNote(true);        
+
+
+            e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
+                    MotionEvent.ACTION_DOWN, 0, 0, 0);
+            e.setIntermediateNote(true);
             mQ.addLast(e);
-            
-            
-            e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt, 
-                    MotionEvent.ACTION_UP, 0, 0, 0);        
-            e.setIntermediateNote(false);        
+
+
+            e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
+                    MotionEvent.ACTION_UP, 0, 0, 0);
+            e.setIntermediateNote(false);
             mQ.addLast(e);
-        }        
+        }
     }
-    
-    /** 
+
+    /**
      * generate a random event based on mFactor
      */
-    private void generateEvents() {        
+    private void generateEvents() {
         float cls = mRandom.nextFloat();
         int lastKey = 0;
 
         boolean touchEvent = cls < mFactors[FACTOR_TOUCH];
         boolean motionEvent = !touchEvent && (cls < mFactors[FACTOR_MOTION]);
-        if (touchEvent || motionEvent) {            
+        if (touchEvent || motionEvent) {
             generateMotionEvent(mRandom, motionEvent);
             return;
         }
-        
-        if (cls < mFactors[FACTOR_TRACKBALL]) {            
+
+        if (cls < mFactors[FACTOR_TRACKBALL]) {
             generateTrackballEvent(mRandom);
             return;
         }
@@ -427,23 +443,23 @@ public class MonkeySourceRandom implements MonkeyEventSource {
         } else {
             lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
         }
-                
+
         MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);
         mQ.addLast(e);
-        
+
         e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
         mQ.addLast(e);
     }
-    
+
     public boolean validate() {
         //check factors
         return adjustEventFactors();
     }
-    
+
     public void setVerbose(int verbose) {
         mVerbose = verbose;
     }
-    
+
     /**
      * generate an activity event
      */
@@ -452,18 +468,18 @@ public class MonkeySourceRandom implements MonkeyEventSource {
                 mRandom.nextInt(mMainApps.size())));
         mQ.addLast(e);
     }
-    
+
     /**
      * if the queue is empty, we generate events first
-     * @return the first event in the queue 
+     * @return the first event in the queue
      */
     public MonkeyEvent getNextEvent() {
         if (mQ.isEmpty()) {
                 generateEvents();
-        }        
-        mEventCount++;        
-        MonkeyEvent e = mQ.getFirst();        
-        mQ.removeFirst();        
+        }
+        mEventCount++;
+        MonkeyEvent e = mQ.getFirst();
+        mQ.removeFirst();
         return e;
     }
 }
index e117528..381c5da 100644 (file)
@@ -1,8 +1,8 @@
 <html>
 <head>
-<meta http-equiv="refresh" content="0;url=docs/sdk/1.1_r1/index.html">
+<meta http-equiv="refresh" content="0;url=docs/sdk/RELEASENOTES.html">
 </head>
 <body>
-<a href="docs/sdk/1.1_r1/index.html">click here if you are not redirected</a>
+<a href="docs/sdk/RELEASENOTES.html">click here if you are not redirected</a>
 </body>
-</html>
\ No newline at end of file
+</html>
index 19b3764..b9fde22 100644 (file)
Binary files a/emulator/skins/HVGA/arrow_down.png and b/emulator/skins/HVGA/arrow_down.png differ
index 113e584..281b192 100644 (file)
Binary files a/emulator/skins/HVGA/arrow_left.png and b/emulator/skins/HVGA/arrow_left.png differ
index ffe3356..4cbc65d 100644 (file)
Binary files a/emulator/skins/HVGA/arrow_right.png and b/emulator/skins/HVGA/arrow_right.png differ
index 81c54df..29c7121 100644 (file)
Binary files a/emulator/skins/HVGA/arrow_up.png and b/emulator/skins/HVGA/arrow_up.png differ
diff --git a/emulator/skins/HVGA/back.png b/emulator/skins/HVGA/back.png
deleted file mode 100644 (file)
index 41034d9..0000000
Binary files a/emulator/skins/HVGA/back.png and /dev/null differ
diff --git a/emulator/skins/HVGA/background_land.png b/emulator/skins/HVGA/background_land.png
new file mode 100644 (file)
index 0000000..09c857b
Binary files /dev/null and b/emulator/skins/HVGA/background_land.png differ
diff --git a/emulator/skins/HVGA/background_port.png b/emulator/skins/HVGA/background_port.png
new file mode 100644 (file)
index 0000000..a452934
Binary files /dev/null and b/emulator/skins/HVGA/background_port.png differ
diff --git a/emulator/skins/HVGA/button.png b/emulator/skins/HVGA/button.png
new file mode 100644 (file)
index 0000000..8281d20
Binary files /dev/null and b/emulator/skins/HVGA/button.png differ
diff --git a/emulator/skins/HVGA/controls.png b/emulator/skins/HVGA/controls.png
new file mode 100644 (file)
index 0000000..04b85e2
Binary files /dev/null and b/emulator/skins/HVGA/controls.png differ
diff --git a/emulator/skins/HVGA/device.png b/emulator/skins/HVGA/device.png
deleted file mode 100644 (file)
index 465eb02..0000000
Binary files a/emulator/skins/HVGA/device.png and /dev/null differ
diff --git a/emulator/skins/HVGA/end.png b/emulator/skins/HVGA/end.png
deleted file mode 100644 (file)
index 6830a60..0000000
Binary files a/emulator/skins/HVGA/end.png and /dev/null differ
diff --git a/emulator/skins/HVGA/home.png b/emulator/skins/HVGA/home.png
deleted file mode 100644 (file)
index 7d02136..0000000
Binary files a/emulator/skins/HVGA/home.png and /dev/null differ
index 7a3f563..40b03bf 100644 (file)
Binary files a/emulator/skins/HVGA/key.png and b/emulator/skins/HVGA/key.png differ
index bb076d3..ca49dcf 100644 (file)
Binary files a/emulator/skins/HVGA/keyboard.png and b/emulator/skins/HVGA/keyboard.png differ
index 4c3d764..7117824 100644 (file)
 parts {
-    device {
+    portrait {
         background {
-            image   device.png
+            image   background_port.png
         }
+    }
+    landscape {
+        background {
+            image   background_land.png
+        }
+    }
+
+    device {
         display {
             width   320
             height  480
-            x       31
-            y       72
+            x       0
+            y       0
+        }
+    }
+    
+    controls {
+        background {
+            image   controls.png
         }
-
         buttons {
             soft-left {
-                    image menu.png
-                    x 147
-                    y 555
+                    image button.png
+                    x 56
+                    y 142
             }
             home {
-                    image home.png
-                    x 48
-                    y 590
+                    image button.png
+                    x 0
+                    y 142
             }
             back {
-                    image back.png
-                    x 286
-                    y 590
+                    image button.png
+                    x 112
+                    y 142
             }
             dpad-up {
                     image arrow_up.png
-                    x 140
-                    y 595
+                    x 77
+                    y 53
             }
             dpad-down {
                     image arrow_down.png
-                    x 140
-                    y 656
+                    x 77
+                    y 106
             }
             dpad-left {
                     image arrow_left.png
-                    x 111
-                    y 598
+                    x 53
+                    y 53
             }
             dpad-right {
                     image arrow_right.png
-                    x 222
-                    y 598
+                    x 123
+                    y 53
             }
             dpad-center {
                     image select.png
-                    x 142
-                    y 626
+                    x 77
+                    y 81
             }
             phone-dial {
-                    image send.png
-                    x 48
-                    y 646
+                    image button.png
+                    x 0
+                    y 71
             }
             phone-hangup {
-                    image end.png
-                    x 286
-                    y 646
+                    image button.png
+                    x 168
+                    y 71
             }
 
             power {
-                    image power.png
-                    x -38
-                    y 52
+                    image button.png
+                    x 168
+                    y 0
             }
 
             volume-up {
-                    image volume_up.png
-                    x 362
-                    y 260
+                    image button.png
+                    x 112
+                    y 0
             }
 
             volume-down {
-                    image volume_down.png
-                    x 362
-                    y 310
+                    image button.png
+                    x 56
+                    y 0
             }
+
+            search {
+                    image button.png
+                    x 168
+                    y 142
+            }
+
         }
     }
 
@@ -89,242 +109,242 @@ parts {
         buttons {
             1 {
                 image  key.png
-                x  0
-                y  0
+                x  5
+                y  5
             }
             2 {
                 image  key.png
-                x 37
-                y 0
+                x 42
+                y 5
             }
             3 {
                 image  key.png
-                x 74
-                y 0
+                x 79
+                y 5
             }
             4 {
                 image  key.png
-                x 111
-                y 0
+                x 116
+                y 5
             }
             5 {
                 image  key.png
-                x 148
-                y 0
+                x 153
+                y 5
             }
             6 {
                 image  key.png
-                x 185
-                y 0
+                x 190
+                y 5
             }
             7 {
                 image  key.png
-                x 222
-                y 0
+                x 227
+                y 5
             }
             8 {
                 image  key.png
-                x 259
-                y 0
+                x 264
+                y 5
             }
             9 {
                 image  key.png
-                x 296
-                y 0
+                x 301
+                y 5
             }
             0 {
                 image  key.png
-                x 333
-                y 0
+                x 338
+                y 5
             }
 
             q {
                 image  key.png
-                x  0
-                y  36
+                x  5
+                y  41
             }
             w {
                 image  key.png
-                x 37
-                y 36
+                x 42
+                y 41
             }
             e {
                 image  key.png
-                x 74
-                y 36
+                x 79
+                y 41
             }
             r {
                 image  key.png
-                x 111
-                y 36
+                x 116
+                y 41
             }
             t {
                 image  key.png
-                x 148
-                y 36
+                x 153
+                y 41
             }
             y {
                 image  key.png
-                x 185
-                y 36
+                x 190
+                y 41
             }
             u {
                 image  key.png
-                x 222
-                y 36
+                x 227
+                y 41
             }
             i {
                 image  key.png
-                x 259
-                y 36
+                x 264
+                y 41
             }
             o {
                 image  key.png
-                x 296
-                y 36
+                x 301
+                y 41
             }
             p {
                 image  key.png
-                x 333
-                y 36
+                x 338
+                y 41
             }
 
             a {
                 image  key.png
-                x  0
-                y  72
+                x  5
+                y 77
             }
             s {
                 image  key.png
-                x 37
-                y 72
+                x 42
+                y 77
             }
             d {
                 image  key.png
-                x 74
-                y 72
+                x 79
+                y 77
             }
             f {
                 image  key.png
-                x 111
-                y 72
+                x 116
+                y 77
             }
             g {
                 image  key.png
-                x 148
-                y 72
+                x 153
+                y 77
             }
             h {
                 image  key.png
-                x 185
-                y 72
+                x 190
+                y 77
             }
             j {
                 image  key.png
-                x 222
-                y 72
+                x 227
+                y 77
             }
             k {
                 image  key.png
-                x 259
-                y 72
+                x 264
+                y 77
             }
             l {
                 image  key.png
-                x 296
-                y 72
+                x 301
+                y 77
             }
             DEL {
                 image  key.png
-                x 333
-                y 72
+                x 338
+                y 77
             }
 
             CAP {
                 image  key.png
-                x  0
-                y  108
+                x  5
+                y 113
             }
             z {
                 image  key.png
-                x 37
-                y 108
+                x 42
+                y 113
             }
             x {
                 image  key.png
-                x 74
-                y 108
+                x 79
+                y 113
             }
             c {
                 image  key.png
-                x 111
-                y 108
+                x 116
+                y 113
             }
             v {
                 image  key.png
-                x 148
-                y 108
+                x 153
+                y 113
             }
             b {
                 image  key.png
-                x 185
-                y 108
+                x 190
+                y 113
             }
             n {
                 image  key.png
-                x 222
-                y 108
+                x 227
+                y 113
             }
             m {
                 image  key.png
-                x 259
-                y 108
+                x 264
+                y 113
             }
             PERIOD {
                 image  key.png
-                x 296
-                y 108
+                x 301
+                y 113
             }
             ENTER {
                 image  key.png
-                x 333
-                y 108
+                x 338
+                y 113
             }
 
             ALT {
                 image  key.png
-                x  0
-                y  144
+                x  5
+                y 149
             }
             SYM {
                 image  key.png
-                x 37
-                y 144
+                x 42
+                y 149
             }
             AT {
                 image  key.png
-                x 74
-                y 144
+                x 79
+                y 149
             }
             SPACE {
                 image  spacebar.png
-                x 111
-                y 144
+                x 116
+                y 149
             }
             SLASH {
                 image  key.png
-                x 259
-                y 144
+                x 264
+                y 149
             }
             COMMA {
                 image  key.png
-                x 296
-                y 144
+                x 301
+                y 149
             }
             ALT2 {
                 image  key.png
-                x 333
-                y 144
+                x 338
+                y 149
             }
 
         }
@@ -333,39 +353,76 @@ parts {
 
 layouts {
     portrait {
-        width     900
-        height    730
+        width     791
+        height    534
         color     0xe0e0e0
         event     EV_SW:0:1
-
+        
         part1 {
-            name    device
-            x       40
-            y       -18
+            name    portrait
+            x       0
+            y       0
         }
+
         part2 {
+            name    landscape
+            x       800
+            y       0
+        }
+
+        part3 {
+            name    device
+            x       28
+            y       27
+        }
+        part4 {
+            name    controls
+            x       476
+            y       77
+        }
+        part5 {
             name    keyboard
-            x       480
-            y       200
+            x       395
+            y       328
         }
+
     }
 
     landscape {
-        width     900
-        height    670
+        width     640
+        height    601
         color     0xe0e0e0
         event     EV_SW:0:0
 
         part1 {
+            name    portrait
+            x       800
+            y       0
+        }
+
+        part2 {
+            name    landscape
+            x       0
+            y       0
+        }
+
+        part3 {
             name      device
-            x         50
-            y         440
+            x         80
+            y         349
             rotation  3
         }
-        part2 {
+
+        part4 {
+            name     controls
+            x        410
+            y        396
+        }
+
+        part5 {
             name     keyboard
-            x        250
-            y        470
+            x        18
+            y        396
         }
     }
 }
diff --git a/emulator/skins/HVGA/menu.png b/emulator/skins/HVGA/menu.png
deleted file mode 100644 (file)
index e81d8ab..0000000
Binary files a/emulator/skins/HVGA/menu.png and /dev/null differ
diff --git a/emulator/skins/HVGA/power.png b/emulator/skins/HVGA/power.png
deleted file mode 100644 (file)
index 5894288..0000000
Binary files a/emulator/skins/HVGA/power.png and /dev/null differ
index 803d493..f4a65d3 100644 (file)
Binary files a/emulator/skins/HVGA/select.png and b/emulator/skins/HVGA/select.png differ
diff --git a/emulator/skins/HVGA/send.png b/emulator/skins/HVGA/send.png
deleted file mode 100644 (file)
index f547c88..0000000
Binary files a/emulator/skins/HVGA/send.png and /dev/null differ
index 19fe604..aa459bd 100644 (file)
Binary files a/emulator/skins/HVGA/spacebar.png and b/emulator/skins/HVGA/spacebar.png differ
diff --git a/emulator/skins/HVGA/volume_down.png b/emulator/skins/HVGA/volume_down.png
deleted file mode 100644 (file)
index f8a88de..0000000
Binary files a/emulator/skins/HVGA/volume_down.png and /dev/null differ
diff --git a/emulator/skins/HVGA/volume_up.png b/emulator/skins/HVGA/volume_up.png
deleted file mode 100644 (file)
index 940457f..0000000
Binary files a/emulator/skins/HVGA/volume_up.png and /dev/null differ
diff --git a/emulator/skins/QVGA/.DS_Store b/emulator/skins/QVGA/.DS_Store
deleted file mode 100644 (file)
index 6aff2f6..0000000
Binary files a/emulator/skins/QVGA/.DS_Store and /dev/null differ
index 7398bae..b9fde22 100644 (file)
Binary files a/emulator/skins/QVGA/arrow_down.png and b/emulator/skins/QVGA/arrow_down.png differ
index f7e3c12..281b192 100644 (file)
Binary files a/emulator/skins/QVGA/arrow_left.png and b/emulator/skins/QVGA/arrow_left.png differ
index 33fa169..4cbc65d 100644 (file)
Binary files a/emulator/skins/QVGA/arrow_right.png and b/emulator/skins/QVGA/arrow_right.png differ
index f21105a..29c7121 100644 (file)
Binary files a/emulator/skins/QVGA/arrow_up.png and b/emulator/skins/QVGA/arrow_up.png differ
diff --git a/emulator/skins/QVGA/back.png b/emulator/skins/QVGA/back.png
deleted file mode 100644 (file)
index 8519ebd..0000000
Binary files a/emulator/skins/QVGA/back.png and /dev/null differ
diff --git a/emulator/skins/QVGA/background_land.png b/emulator/skins/QVGA/background_land.png
new file mode 100644 (file)
index 0000000..cb002b5
Binary files /dev/null and b/emulator/skins/QVGA/background_land.png differ
diff --git a/emulator/skins/QVGA/background_port.png b/emulator/skins/QVGA/background_port.png
new file mode 100644 (file)
index 0000000..6728562
Binary files /dev/null and b/emulator/skins/QVGA/background_port.png differ
diff --git a/emulator/skins/QVGA/button.png b/emulator/skins/QVGA/button.png
new file mode 100644 (file)
index 0000000..8281d20
Binary files /dev/null and b/emulator/skins/QVGA/button.png differ
diff --git a/emulator/skins/QVGA/controls.png b/emulator/skins/QVGA/controls.png
new file mode 100644 (file)
index 0000000..04b85e2
Binary files /dev/null and b/emulator/skins/QVGA/controls.png differ
diff --git a/emulator/skins/QVGA/device.png b/emulator/skins/QVGA/device.png
deleted file mode 100644 (file)
index eb64104..0000000
Binary files a/emulator/skins/QVGA/device.png and /dev/null differ
diff --git a/emulator/skins/QVGA/end.png b/emulator/skins/QVGA/end.png
deleted file mode 100644 (file)
index 8519ebd..0000000
Binary files a/emulator/skins/QVGA/end.png and /dev/null differ
diff --git a/emulator/skins/QVGA/home.png b/emulator/skins/QVGA/home.png
deleted file mode 100644 (file)
index 8519ebd..0000000
Binary files a/emulator/skins/QVGA/home.png and /dev/null differ
index 7a3f563..40b03bf 100644 (file)
Binary files a/emulator/skins/QVGA/key.png and b/emulator/skins/QVGA/key.png differ
index bb076d3..ca49dcf 100644 (file)
Binary files a/emulator/skins/QVGA/keyboard.png and b/emulator/skins/QVGA/keyboard.png differ
index d7539c5..f98e7bd 100644 (file)
 parts {
-    device {
+    portrait {
+        background {
+            image   background_port.png
+        }
+    }
+    landscape {
         background {
-            image   device.png
+            image   background_land.png
         }
+    }
+
+    device {
         display {
             width   240
             height  320
-            x       33
-            y       69
+            x       0
+            y       0
+        }
+    }
+    
+    controls {
+        background {
+            image   controls.png
         }
-
         buttons {
             soft-left {
-                    image menu.png
-                    x 131
-                    y 404
+                    image button.png
+                    x 56
+                    y 142
             }
             home {
-                    image home.png
-                    x 77
-                    y 404
+                    image button.png
+                    x 0
+                    y 142
             }
             back {
-                    image back.png
-                    x 185
-                    y 404
+                    image button.png
+                    x 112
+                    y 142
             }
             dpad-up {
                     image arrow_up.png
-                    x 105
-                    y 463
+                    x 77
+                    y 53
             }
             dpad-down {
                     image arrow_down.png
-                    x 104
-                    y 519
+                    x 77
+                    y 106
             }
             dpad-left {
                     image arrow_left.png
-                    x 96
-                    y 470
+                    x 53
+                    y 53
             }
             dpad-right {
                     image arrow_right.png
-                    x 172
-                    y 470
+                    x 123
+                    y 53
             }
             dpad-center {
                     image select.png
-                    x 131
-                    y 492
+                    x 77
+                    y 81
             }
             phone-dial {
-                    image send.png
-                    x 23
-                    y 404
+                    image button.png
+                    x 0
+                    y 71
             }
             phone-hangup {
-                    image end.png
-                    x 238
-                    y 404
+                    image button.png
+                    x 168
+                    y 71
             }
 
             power {
-                    image power.png
-                    x -16
+                    image button.png
+                    x 168
                     y 0
             }
 
             volume-up {
-                    image volume_up.png
-                    x 289
-                    y 177
+                    image button.png
+                    x 112
+                    y 0
             }
 
             volume-down {
-                    image volume_down.png
-                    x 289
-                    y 233
+                    image button.png
+                    x 56
+                    y 0
+            }
+
+            search {
+                    image button.png
+                    x 168
+                    y 142
             }
+
         }
     }
 
@@ -89,242 +109,242 @@ parts {
         buttons {
             1 {
                 image  key.png
-                x  0
-                y  0
+                x  5
+                y  5
             }
             2 {
                 image  key.png
-                x 37
-                y 0
+                x 42
+                y 5
             }
             3 {
                 image  key.png
-                x 74
-                y 0
+                x 79
+                y 5
             }
             4 {
                 image  key.png
-                x 111
-                y 0
+                x 116
+                y 5
             }
             5 {
                 image  key.png
-                x 148
-                y 0
+                x 153
+                y 5
             }
             6 {
                 image  key.png
-                x 185
-                y 0
+                x 190
+                y 5
             }
             7 {
                 image  key.png
-                x 222
-                y 0
+                x 227
+                y 5
             }
             8 {
                 image  key.png
-                x 259
-                y 0
+                x 264
+                y 5
             }
             9 {
                 image  key.png
-                x 296
-                y 0
+                x 301
+                y 5
             }
             0 {
                 image  key.png
-                x 333
-                y 0
+                x 338
+                y 5
             }
 
             q {
                 image  key.png
-                x  0
-                y  36
+                x  5
+                y  41
             }
             w {
                 image  key.png
-                x 37
-                y 36
+                x 42
+                y 41
             }
             e {
                 image  key.png
-                x 74
-                y 36
+                x 79
+                y 41
             }
             r {
                 image  key.png
-                x 111
-                y 36
+                x 116
+                y 41
             }
             t {
                 image  key.png
-                x 148
-                y 36
+                x 153
+                y 41
             }
             y {
                 image  key.png
-                x 185
-                y 36
+                x 190
+                y 41
             }
             u {
                 image  key.png
-                x 222
-                y 36
+                x 227
+                y 41
             }
             i {
                 image  key.png
-                x 259
-                y 36
+                x 264
+                y 41
             }
             o {
                 image  key.png
-                x 296
-                y 36
+                x 301
+                y 41
             }
             p {
                 image  key.png
-                x 333
-                y 36
+                x 338
+                y 41
             }
 
             a {
                 image  key.png
-                x  0
-                y  72
+                x  5
+                y 77
             }
             s {
                 image  key.png
-                x 37
-                y 72
+                x 42
+                y 77
             }
             d {
                 image  key.png
-                x 74
-                y 72
+                x 79
+                y 77
             }
             f {
                 image  key.png
-                x 111
-                y 72
+                x 116
+                y 77
             }
             g {
                 image  key.png
-                x 148
-                y 72
+                x 153
+                y 77
             }
             h {
                 image  key.png
-                x 185
-                y 72
+                x 190
+                y 77
             }
             j {
                 image  key.png
-                x 222
-                y 72
+                x 227
+                y 77
             }
             k {
                 image  key.png
-                x 259
-                y 72
+                x 264
+                y 77
             }
             l {
                 image  key.png
-                x 296
-                y 72
+                x 301
+                y 77
             }
             DEL {
                 image  key.png
-                x 333
-                y 72
+                x 338
+                y 77
             }
 
             CAP {
                 image  key.png
-                x  0
-                y  108
+                x  5
+                y 113
             }
             z {
                 image  key.png
-                x 37
-                y 108
+                x 42
+                y 113
             }
             x {
                 image  key.png
-                x 74
-                y 108
+                x 79
+                y 113
             }
             c {
                 image  key.png
-                x 111
-                y 108
+                x 116
+                y 113
             }
             v {
                 image  key.png
-                x 148
-                y 108
+                x 153
+                y 113
             }
             b {
                 image  key.png
-                x 185
-                y 108
+                x 190
+                y 113
             }
             n {
                 image  key.png
-                x 222
-                y 108
+                x 227
+                y 113
             }
             m {
                 image  key.png
-                x 259
-                y 108
+                x 264
+                y 113
             }
             PERIOD {
                 image  key.png
-                x 296
-                y 108
+                x 301
+                y 113
             }
             ENTER {
                 image  key.png
-                x 333
-                y 108
+                x 338
+                y 113
             }
 
             ALT {
                 image  key.png
-                x  0
-                y  144
+                x  5
+                y 149
             }
             SYM {
                 image  key.png
-                x 37
-                y 144
+                x 42
+                y 149
             }
             AT {
                 image  key.png
-                x 74
-                y 144
+                x 79
+                y 149
             }
             SPACE {
                 image  spacebar.png
-                x 111
-                y 144
+                x 116
+                y 149
             }
             SLASH {
                 image  key.png
-                x 259
-                y 144
+                x 264
+                y 149
             }
             COMMA {
                 image  key.png
-                x 296
-                y 144
+                x 301
+                y 149
             }
             ALT2 {
                 image  key.png
-                x 333
-                y 144
+                x 338
+                y 149
             }
 
         }
@@ -333,39 +353,75 @@ parts {
 
 layouts {
     portrait {
-        width     750
-        height    610
+        width     711
+        height    435
         color     0xe0e0e0
         event     EV_SW:0:1
-
+        
         part1 {
-            name    device
-            x       30
-            y       097
+            name    portrait
+            x       0
+            y       0
         }
+
         part2 {
-            name    keyboard
-            x       360
-            y       300
+            name    landscape
+            x       800
+            y       0
+        }
+
+        part3 {
+            name    device
+            x       28
+            y       58
+        }
+        part4 {
+            name     controls
+            x        396
+            y        27
+        }
+        part5 {
+            name     keyboard
+            x        315
+            y        229
         }
     }
 
     landscape {
-        width     645
-        height    575
+        width     640
+        height    522
         color     0xe0e0e0
         event     EV_SW:0:0
 
         part1 {
+            name    portrait
+            x       800
+            y       0
+        }
+
+        part2 {
+            name    landscape
+            x       0
+            y       0
+        }
+
+        part3 {
             name      device
-            x         10
-            y         360
+            x         160
+            y         270
             rotation  3
         }
-        part2 {
+
+        part4 {
+            name     controls
+            x        410
+            y        317
+        }
+
+        part5 {
             name     keyboard
-            x        135
-            y        380
+            x        18
+            y        317
         }
     }
 }
diff --git a/emulator/skins/QVGA/menu.png b/emulator/skins/QVGA/menu.png
deleted file mode 100644 (file)
index 8519ebd..0000000
Binary files a/emulator/skins/QVGA/menu.png and /dev/null differ
diff --git a/emulator/skins/QVGA/power.png b/emulator/skins/QVGA/power.png
deleted file mode 100644 (file)
index 0c04ced..0000000
Binary files a/emulator/skins/QVGA/power.png and /dev/null differ
index 8691f53..f4a65d3 100644 (file)
Binary files a/emulator/skins/QVGA/select.png and b/emulator/skins/QVGA/select.png differ
diff --git a/emulator/skins/QVGA/send.png b/emulator/skins/QVGA/send.png
deleted file mode 100644 (file)
index 8519ebd..0000000
Binary files a/emulator/skins/QVGA/send.png and /dev/null differ
index 19fe604..aa459bd 100644 (file)
Binary files a/emulator/skins/QVGA/spacebar.png and b/emulator/skins/QVGA/spacebar.png differ
diff --git a/emulator/skins/QVGA/volume_down.png b/emulator/skins/QVGA/volume_down.png
deleted file mode 100644 (file)
index 09175b1..0000000
Binary files a/emulator/skins/QVGA/volume_down.png and /dev/null differ
diff --git a/emulator/skins/QVGA/volume_up.png b/emulator/skins/QVGA/volume_up.png
deleted file mode 100644 (file)
index ab52c63..0000000
Binary files a/emulator/skins/QVGA/volume_up.png and /dev/null differ
diff --git a/emulator/skins/WQVGA432/arrow_down.png b/emulator/skins/WQVGA432/arrow_down.png
new file mode 100644 (file)
index 0000000..b9fde22
Binary files /dev/null and b/emulator/skins/WQVGA432/arrow_down.png differ
diff --git a/emulator/skins/WQVGA432/arrow_left.png b/emulator/skins/WQVGA432/arrow_left.png
new file mode 100644 (file)
index 0000000..281b192
Binary files /dev/null and b/emulator/skins/WQVGA432/arrow_left.png differ
diff --git a/emulator/skins/WQVGA432/arrow_right.png b/emulator/skins/WQVGA432/arrow_right.png
new file mode 100644 (file)
index 0000000..4cbc65d
Binary files /dev/null and b/emulator/skins/WQVGA432/arrow_right.png differ
diff --git a/emulator/skins/WQVGA432/arrow_up.png b/emulator/skins/WQVGA432/arrow_up.png
new file mode 100644 (file)
index 0000000..29c7121
Binary files /dev/null and b/emulator/skins/WQVGA432/arrow_up.png differ
diff --git a/emulator/skins/WQVGA432/background_land.png b/emulator/skins/WQVGA432/background_land.png
new file mode 100644 (file)
index 0000000..1450e25
Binary files /dev/null and b/emulator/skins/WQVGA432/background_land.png differ
diff --git a/emulator/skins/WQVGA432/background_port.png b/emulator/skins/WQVGA432/background_port.png
new file mode 100644 (file)
index 0000000..891ab45
Binary files /dev/null and b/emulator/skins/WQVGA432/background_port.png differ
diff --git a/emulator/skins/WQVGA432/button.png b/emulator/skins/WQVGA432/button.png
new file mode 100644 (file)
index 0000000..8281d20
Binary files /dev/null and b/emulator/skins/WQVGA432/button.png differ
diff --git a/emulator/skins/WQVGA432/controls.png b/emulator/skins/WQVGA432/controls.png
new file mode 100644 (file)
index 0000000..04b85e2
Binary files /dev/null and b/emulator/skins/WQVGA432/controls.png differ
diff --git a/emulator/skins/WQVGA432/hardware.ini b/emulator/skins/WQVGA432/hardware.ini
new file mode 100644 (file)
index 0000000..2efe617
--- /dev/null
@@ -0,0 +1,2 @@
+# skin-specific hardware values
+hw.lcd.density=120
\ No newline at end of file
diff --git a/emulator/skins/WQVGA432/key.png b/emulator/skins/WQVGA432/key.png
new file mode 100644 (file)
index 0000000..40b03bf
Binary files /dev/null and b/emulator/skins/WQVGA432/key.png differ
diff --git a/emulator/skins/WQVGA432/keyboard.png b/emulator/skins/WQVGA432/keyboard.png
new file mode 100644 (file)
index 0000000..ca49dcf
Binary files /dev/null and b/emulator/skins/WQVGA432/keyboard.png differ
diff --git a/emulator/skins/WQVGA432/layout b/emulator/skins/WQVGA432/layout
new file mode 100644 (file)
index 0000000..7e52b53
--- /dev/null
@@ -0,0 +1,436 @@
+parts {
+    portrait {
+        background {
+            image   background_port.png
+        }
+    }
+    landscape {
+        background {
+            image   background_land.png
+        }
+    }
+
+    device {
+        display {
+            width   240
+            height  432
+            x       0
+            y       0
+        }
+    }
+    
+    controls {
+        background {
+            image   controls.png
+        }
+        buttons {
+            soft-left {
+                    image button.png
+                    x 56
+                    y 142
+            }
+            home {
+                    image button.png
+                    x 0
+                    y 142
+            }
+            back {
+                    image button.png
+                    x 112
+                    y 142
+            }
+            dpad-up {
+                    image arrow_up.png
+                    x 77
+                    y 53
+            }
+            dpad-down {
+                    image arrow_down.png
+                    x 77
+                    y 106
+            }
+            dpad-left {
+                    image arrow_left.png
+                    x 53
+                    y 53
+            }
+            dpad-right {
+                    image arrow_right.png
+                    x 123
+                    y 53
+            }
+            dpad-center {
+                    image select.png
+                    x 77
+                    y 81
+            }
+            phone-dial {
+                    image button.png
+                    x 0
+                    y 71
+            }
+            phone-hangup {
+                    image button.png
+                    x 168
+                    y 71
+            }
+
+            power {
+                    image button.png
+                    x 168
+                    y 0
+            }
+
+            volume-up {
+                    image button.png
+                    x 112
+                    y 0
+            }
+
+            volume-down {
+                    image button.png
+                    x 56
+                    y 0
+            }
+
+            search {
+                    image button.png
+                    x 168
+                    y 142
+            }
+
+        }
+    }
+
+    keyboard {
+        background {
+            image   keyboard.png
+        }
+        buttons {
+            1 {
+                image  key.png
+                x  5
+                y  5
+            }
+            2 {
+                image  key.png
+                x 42
+                y 5
+            }
+            3 {
+                image  key.png
+                x 79
+                y 5
+            }
+            4 {
+                image  key.png
+                x 116
+                y 5
+            }
+            5 {
+                image  key.png
+                x 153
+                y 5
+            }
+            6 {
+                image  key.png
+                x 190
+                y 5
+            }
+            7 {
+                image  key.png
+                x 227
+                y 5
+            }
+            8 {
+                image  key.png
+                x 264
+                y 5
+            }
+            9 {
+                image  key.png
+                x 301
+                y 5
+            }
+            0 {
+                image  key.png
+                x 338
+                y 5
+            }
+
+            q {
+                image  key.png
+                x  5
+                y  41
+            }
+            w {
+                image  key.png
+                x 42
+                y 41
+            }
+            e {
+                image  key.png
+                x 79
+                y 41
+            }
+            r {
+                image  key.png
+                x 116
+                y 41
+            }
+            t {
+                image  key.png
+                x 153
+                y 41
+            }
+            y {
+                image  key.png
+                x 190
+                y 41
+            }
+            u {
+                image  key.png
+                x 227
+                y 41
+            }
+            i {
+                image  key.png
+                x 264
+                y 41
+            }
+            o {
+                image  key.png
+                x 301
+                y 41
+            }
+            p {
+                image  key.png
+                x 338
+                y 41
+            }
+
+            a {
+                image  key.png
+                x  5
+                y 77
+            }
+            s {
+                image  key.png
+                x 42
+                y 77
+            }
+            d {
+                image  key.png
+                x 79
+                y 77
+            }
+            f {
+                image  key.png
+                x 116
+                y 77
+            }
+            g {
+                image  key.png
+                x 153
+                y 77
+            }
+            h {
+                image  key.png
+                x 190
+                y 77
+            }
+            j {
+                image  key.png
+                x 227
+                y 77
+            }
+            k {
+                image  key.png
+                x 264
+                y 77
+            }
+            l {
+                image  key.png
+                x 301
+                y 77
+            }
+            DEL {
+                image  key.png
+                x 338
+                y 77
+            }
+
+            CAP {
+                image  key.png
+                x  5
+                y 113
+            }
+            z {
+                image  key.png
+                x 42
+                y 113
+            }
+            x {
+                image  key.png
+                x 79
+                y 113
+            }
+            c {
+                image  key.png
+                x 116
+                y 113
+            }
+            v {
+                image  key.png
+                x 153
+                y 113
+            }
+            b {
+                image  key.png
+                x 190
+                y 113
+            }
+            n {
+                image  key.png
+                x 227
+                y 113
+            }
+            m {
+                image  key.png
+                x 264
+                y 113
+            }
+            PERIOD {
+                image  key.png
+                x 301
+                y 113
+            }
+            ENTER {
+                image  key.png
+                x 338
+                y 113
+            }
+
+            ALT {
+                image  key.png
+                x  5
+                y 149
+            }
+            SYM {
+                image  key.png
+                x 42
+                y 149
+            }
+            AT {
+                image  key.png
+                x 79
+                y 149
+            }
+            SPACE {
+                image  spacebar.png
+                x 116
+                y 149
+            }
+            SLASH {
+                image  key.png
+                x 264
+                y 149
+            }
+            COMMA {
+                image  key.png
+                x 301
+                y 149
+            }
+            ALT2 {
+                image  key.png
+                x 338
+                y 149
+            }
+
+        }
+    }
+}
+
+layouts {
+    portrait {
+        width     711
+        height    486
+        color     0xe0e0e0
+        event     EV_SW:0:1
+        
+        part1 {
+            name    portrait
+            x       0
+            y       0
+        }
+
+        part2 {
+            name    landscape
+            x       800
+            y       0
+        }
+
+        part3 {
+            name    device
+            x       28
+            y       27
+        }
+        part4 {
+            name     controls
+            x        396
+            y        53
+        }
+        part5 {
+            name     keyboard
+            x        315
+            y        280
+        }
+    }
+
+    landscape {
+        width     640
+        height    522
+        color     0xe0e0e0
+        event     EV_SW:0:0
+
+        part1 {
+            name    portrait
+            x       800
+            y       0
+        }
+
+        part2 {
+            name    landscape
+            x       0
+            y       0
+        }
+
+        part3 {
+            name      device
+            x         104
+            y         270
+            rotation  3
+        }
+
+        part4 {
+            name     controls
+            x        410
+            y        317
+        }
+
+        part5 {
+            name     keyboard
+            x        18
+            y        317
+        }
+    }
+}
+
+keyboard {
+    charmap qwerty2
+}
+
+network {
+    speed  full
+    delay  none
+}
diff --git a/emulator/skins/WQVGA432/select.png b/emulator/skins/WQVGA432/select.png
new file mode 100644 (file)
index 0000000..f4a65d3
Binary files /dev/null and b/emulator/skins/WQVGA432/select.png differ
diff --git a/emulator/skins/WQVGA432/spacebar.png b/emulator/skins/WQVGA432/spacebar.png
new file mode 100644 (file)
index 0000000..aa459bd
Binary files /dev/null and b/emulator/skins/WQVGA432/spacebar.png differ
diff --git a/emulator/skins/WVGA/arrow_down.png b/emulator/skins/WVGA/arrow_down.png
deleted file mode 100644 (file)
index 19b3764..0000000
Binary files a/emulator/skins/WVGA/arrow_down.png and /dev/null differ
diff --git a/emulator/skins/WVGA/arrow_left.png b/emulator/skins/WVGA/arrow_left.png
deleted file mode 100644 (file)
index 113e584..0000000
Binary files a/emulator/skins/WVGA/arrow_left.png and /dev/null differ
diff --git a/emulator/skins/WVGA/arrow_right.png b/emulator/skins/WVGA/arrow_right.png
deleted file mode 100644 (file)
index ffe3356..0000000
Binary files a/emulator/skins/WVGA/arrow_right.png and /dev/null differ
diff --git a/emulator/skins/WVGA/arrow_up.png b/emulator/skins/WVGA/arrow_up.png
deleted file mode 100644 (file)
index 81c54df..0000000
Binary files a/emulator/skins/WVGA/arrow_up.png and /dev/null differ
diff --git a/emulator/skins/WVGA/back.png b/emulator/skins/WVGA/back.png
deleted file mode 100644 (file)
index 41034d9..0000000
Binary files a/emulator/skins/WVGA/back.png and /dev/null differ
diff --git a/emulator/skins/WVGA/device.png b/emulator/skins/WVGA/device.png
deleted file mode 100644 (file)
index 3657294..0000000
Binary files a/emulator/skins/WVGA/device.png and /dev/null differ
diff --git a/emulator/skins/WVGA/device.pxi b/emulator/skins/WVGA/device.pxi
deleted file mode 100644 (file)
index a6bc6a0..0000000
Binary files a/emulator/skins/WVGA/device.pxi and /dev/null differ
diff --git a/emulator/skins/WVGA/end.png b/emulator/skins/WVGA/end.png
deleted file mode 100644 (file)
index 6830a60..0000000
Binary files a/emulator/skins/WVGA/end.png and /dev/null differ
diff --git a/emulator/skins/WVGA/home.png b/emulator/skins/WVGA/home.png
deleted file mode 100644 (file)
index 7d02136..0000000
Binary files a/emulator/skins/WVGA/home.png and /dev/null differ
diff --git a/emulator/skins/WVGA/key.png b/emulator/skins/WVGA/key.png
deleted file mode 100644 (file)
index 7a3f563..0000000
Binary files a/emulator/skins/WVGA/key.png and /dev/null differ
diff --git a/emulator/skins/WVGA/keyboard.png b/emulator/skins/WVGA/keyboard.png
deleted file mode 100644 (file)
index bb076d3..0000000
Binary files a/emulator/skins/WVGA/keyboard.png and /dev/null differ
diff --git a/emulator/skins/WVGA/menu.png b/emulator/skins/WVGA/menu.png
deleted file mode 100644 (file)
index 41034d9..0000000
Binary files a/emulator/skins/WVGA/menu.png and /dev/null differ
diff --git a/emulator/skins/WVGA/power.png b/emulator/skins/WVGA/power.png
deleted file mode 100644 (file)
index 5894288..0000000
Binary files a/emulator/skins/WVGA/power.png and /dev/null differ
diff --git a/emulator/skins/WVGA/search.png b/emulator/skins/WVGA/search.png
deleted file mode 100644 (file)
index 41034d9..0000000
Binary files a/emulator/skins/WVGA/search.png and /dev/null differ
diff --git a/emulator/skins/WVGA/select.png b/emulator/skins/WVGA/select.png
deleted file mode 100644 (file)
index 803d493..0000000
Binary files a/emulator/skins/WVGA/select.png and /dev/null differ
diff --git a/emulator/skins/WVGA/send.png b/emulator/skins/WVGA/send.png
deleted file mode 100644 (file)
index f547c88..0000000
Binary files a/emulator/skins/WVGA/send.png and /dev/null differ
diff --git a/emulator/skins/WVGA/spacebar.png b/emulator/skins/WVGA/spacebar.png
deleted file mode 100644 (file)
index 19fe604..0000000
Binary files a/emulator/skins/WVGA/spacebar.png and /dev/null differ
diff --git a/emulator/skins/WVGA/volume_down.png b/emulator/skins/WVGA/volume_down.png
deleted file mode 100644 (file)
index f8a88de..0000000
Binary files a/emulator/skins/WVGA/volume_down.png and /dev/null differ
diff --git a/emulator/skins/WVGA/volume_up.png b/emulator/skins/WVGA/volume_up.png
deleted file mode 100644 (file)
index 940457f..0000000
Binary files a/emulator/skins/WVGA/volume_up.png and /dev/null differ
diff --git a/emulator/skins/WVGA800/arrow_down.png b/emulator/skins/WVGA800/arrow_down.png
new file mode 100644 (file)
index 0000000..b9fde22
Binary files /dev/null and b/emulator/skins/WVGA800/arrow_down.png differ
diff --git a/emulator/skins/WVGA800/arrow_left.png b/emulator/skins/WVGA800/arrow_left.png
new file mode 100644 (file)
index 0000000..281b192
Binary files /dev/null and b/emulator/skins/WVGA800/arrow_left.png differ
diff --git a/emulator/skins/WVGA800/arrow_right.png b/emulator/skins/WVGA800/arrow_right.png
new file mode 100644 (file)
index 0000000..4cbc65d
Binary files /dev/null and b/emulator/skins/WVGA800/arrow_right.png differ
diff --git a/emulator/skins/WVGA800/arrow_up.png b/emulator/skins/WVGA800/arrow_up.png
new file mode 100644 (file)
index 0000000..29c7121
Binary files /dev/null and b/emulator/skins/WVGA800/arrow_up.png differ
diff --git a/emulator/skins/WVGA800/background_land.png b/emulator/skins/WVGA800/background_land.png
new file mode 100644 (file)
index 0000000..4967717
Binary files /dev/null and b/emulator/skins/WVGA800/background_land.png differ
diff --git a/emulator/skins/WVGA800/background_port.png b/emulator/skins/WVGA800/background_port.png
new file mode 100644 (file)
index 0000000..13ef2ea
Binary files /dev/null and b/emulator/skins/WVGA800/background_port.png differ
diff --git a/emulator/skins/WVGA800/button.png b/emulator/skins/WVGA800/button.png
new file mode 100644 (file)
index 0000000..8281d20
Binary files /dev/null and b/emulator/skins/WVGA800/button.png differ
diff --git a/emulator/skins/WVGA800/controls.png b/emulator/skins/WVGA800/controls.png
new file mode 100644 (file)
index 0000000..04b85e2
Binary files /dev/null and b/emulator/skins/WVGA800/controls.png differ
diff --git a/emulator/skins/WVGA800/key.png b/emulator/skins/WVGA800/key.png
new file mode 100644 (file)
index 0000000..40b03bf
Binary files /dev/null and b/emulator/skins/WVGA800/key.png differ
diff --git a/emulator/skins/WVGA800/keyboard.png b/emulator/skins/WVGA800/keyboard.png
new file mode 100644 (file)
index 0000000..ca49dcf
Binary files /dev/null and b/emulator/skins/WVGA800/keyboard.png differ
similarity index 50%
rename from emulator/skins/WVGA/layout
rename to emulator/skins/WVGA800/layout
index c33c354..6037ab8 100644 (file)
 parts {
-    device {
+    portrait {
+        background {
+            image   background_port.png
+        }
+    }
+    landscape {
         background {
-            image   device.png
+            image   background_land.png
         }
+    }
+
+    device {
         display {
             width   480
             height  800
-            x       65
-            y       135
+            x       0
+            y       0
+        }
+    }
+    
+    controls {
+        background {
+            image   controls.png
         }
-
         buttons {
             soft-left {
-                    image menu.png
-                    x 222
-                    y 945
+                    image button.png
+                    x 56
+                    y 142
             }
             home {
-                    image home.png
-                    x 106
-                    y 945
+                    image button.png
+                    x 0
+                    y 142
             }
             back {
-                    image back.png
-                    x 341
-                    y 945
-            }
-            search {
-                    image search.png
-                    x 460
-                    y 945
+                    image button.png
+                    x 112
+                    y 142
             }
             dpad-up {
                     image arrow_up.png
-                    x 260
-                    y 1019
+                    x 77
+                    y 53
             }
             dpad-down {
                     image arrow_down.png
-                    x 260
-                    y 1081
+                    x 77
+                    y 106
             }
             dpad-left {
                     image arrow_left.png
-                    x 223
-                    y 1023
+                    x 53
+                    y 53
             }
             dpad-right {
                     image arrow_right.png
-                    x 343
-                    y 1023
+                    x 123
+                    y 53
             }
             dpad-center {
                     image select.png
-                    x 259
-                    y 1049
+                    x 77
+                    y 81
             }
             phone-dial {
-                    image send.png
-                    x 107
-                    y 1043
+                    image button.png
+                    x 0
+                    y 71
             }
             phone-hangup {
-                    image end.png
-                    x 458
-                    y 1043
+                    image button.png
+                    x 168
+                    y 71
             }
 
             power {
-                    image power.png
-                    x -10
-                    y 120
+                    image button.png
+                    x 168
+                    y 0
             }
 
             volume-up {
-                    image volume_up.png
-                    x 570
-                    y 260
+                    image button.png
+                    x 112
+                    y 0
             }
 
             volume-down {
-                    image volume_down.png
-                    x 570
-                    y 310
+                    image button.png
+                    x 56
+                    y 0
             }
+
+            search {
+                    image button.png
+                    x 168
+                    y 142
+            }
+
         }
     }
 
@@ -94,242 +109,242 @@ parts {
         buttons {
             1 {
                 image  key.png
-                x  0
-                y  0
+                x  5
+                y  5
             }
             2 {
                 image  key.png
-                x 37
-                y 0
+                x 42
+                y 5
             }
             3 {
                 image  key.png
-                x 74
-                y 0
+                x 79
+                y 5
             }
             4 {
                 image  key.png
-                x 111
-                y 0
+                x 116
+                y 5
             }
             5 {
                 image  key.png
-                x 148
-                y 0
+                x 153
+                y 5
             }
             6 {
                 image  key.png
-                x 185
-                y 0
+                x 190
+                y 5
             }
             7 {
                 image  key.png
-                x 222
-                y 0
+                x 227
+                y 5
             }
             8 {
                 image  key.png
-                x 259
-                y 0
+                x 264
+                y 5
             }
             9 {
                 image  key.png
-                x 296
-                y 0
+                x 301
+                y 5
             }
             0 {
                 image  key.png
-                x 333
-                y 0
+                x 338
+                y 5
             }
 
             q {
                 image  key.png
-                x  0
-                y  36
+                x  5
+                y  41
             }
             w {
                 image  key.png
-                x 37
-                y 36
+                x 42
+                y 41
             }
             e {
                 image  key.png
-                x 74
-                y 36
+                x 79
+                y 41
             }
             r {
                 image  key.png
-                x 111
-                y 36
+                x 116
+                y 41
             }
             t {
                 image  key.png
-                x 148
-                y 36
+                x 153
+                y 41
             }
             y {
                 image  key.png
-                x 185
-                y 36
+                x 190
+                y 41
             }
             u {
                 image  key.png
-                x 222
-                y 36
+                x 227
+                y 41
             }
             i {
                 image  key.png
-                x 259
-                y 36
+                x 264
+                y 41
             }
             o {
                 image  key.png
-                x 296
-                y 36
+                x 301
+                y 41
             }
             p {
                 image  key.png
-                x 333
-                y 36
+                x 338
+                y 41
             }
 
             a {
                 image  key.png
-                x  0
-                y  72
+                x  5
+                y 77
             }
             s {
                 image  key.png
-                x 37
-                y 72
+                x 42
+                y 77
             }
             d {
                 image  key.png
-                x 74
-                y 72
+                x 79
+                y 77
             }
             f {
                 image  key.png
-                x 111
-                y 72
+                x 116
+                y 77
             }
             g {
                 image  key.png
-                x 148
-                y 72
+                x 153
+                y 77
             }
             h {
                 image  key.png
-                x 185
-                y 72
+                x 190
+                y 77
             }
             j {
                 image  key.png
-                x 222
-                y 72
+                x 227
+                y 77
             }
             k {
                 image  key.png
-                x 259
-                y 72
+                x 264
+                y 77
             }
             l {
                 image  key.png
-                x 296
-                y 72
+                x 301
+                y 77
             }
             DEL {
                 image  key.png
-                x 333
-                y 72
+                x 338
+                y 77
             }
 
             CAP {
                 image  key.png
-                x  0
-                y  108
+                x  5
+                y 113
             }
             z {
                 image  key.png
-                x 37
-                y 108
+                x 42
+                y 113
             }
             x {
                 image  key.png
-                x 74
-                y 108
+                x 79
+                y 113
             }
             c {
                 image  key.png
-                x 111
-                y 108
+                x 116
+                y 113
             }
             v {
                 image  key.png
-                x 148
-                y 108
+                x 153
+                y 113
             }
             b {
                 image  key.png
-                x 185
-                y 108
+                x 190
+                y 113
             }
             n {
                 image  key.png
-                x 222
-                y 108
+                x 227
+                y 113
             }
             m {
                 image  key.png
-                x 259
-                y 108
+                x 264
+                y 113
             }
             PERIOD {
                 image  key.png
-                x 296
-                y 108
+                x 301
+                y 113
             }
             ENTER {
                 image  key.png
-                x 333
-                y 108
+                x 338
+                y 113
             }
 
             ALT {
                 image  key.png
-                x  0
-                y  144
+                x  5
+                y 149
             }
             SYM {
                 image  key.png
-                x 37
-                y 144
+                x 42
+                y 149
             }
             AT {
                 image  key.png
-                x 74
-                y 144
+                x 79
+                y 149
             }
             SPACE {
                 image  spacebar.png
-                x 111
-                y 144
+                x 116
+                y 149
             }
             SLASH {
                 image  key.png
-                x 259
-                y 144
+                x 264
+                y 149
             }
             COMMA {
                 image  key.png
-                x 296
-                y 144
+                x 301
+                y 149
             }
             ALT2 {
                 image  key.png
-                x 333
-                y 144
+                x 338
+                y 149
             }
 
         }
@@ -338,39 +353,76 @@ parts {
 
 layouts {
     portrait {
-        width     975
-        height    1080
-        color     0xffffff
+        width     950
+        height    854
+        color     0xe0e0e0
         event     EV_SW:0:1
-
+        
         part1 {
-            name    device
-            x       10
-            y       -70
+            name    portrait
+            x       0
+            y       0
         }
+
         part2 {
+            name    landscape
+            x       1000
+            y       0
+        }
+
+        part3 {
+            name    device
+            x       27
+            y       27
+        }
+        part4 {
+            name    controls
+            x       635
+            y       207
+        }
+        part5 {
             name    keyboard
-            x       600
-            y       400
+            x       554
+            y       459
         }
+
     }
 
     landscape {
-        width     1080
-        height    810
-        color     0xffffff
+        width     853
+        height    761
+        color     0xe0e0e0
         event     EV_SW:0:0
 
         part1 {
+            name    portrait
+            x       900
+            y       0
+        }
+
+        part2 {
+            name    landscape
+            x       0
+            y       0
+        }
+
+        part3 {
             name      device
-            x         -70
-            y         640
+            x         26
+            y         509
             rotation  3
         }
-        part2 {
+
+        part4 {
+            name     controls
+            x        539
+            y        556
+        }
+
+        part5 {
             name     keyboard
-            x        250
-            y        620
+            x        98
+            y        556
         }
     }
 }
diff --git a/emulator/skins/WVGA800/select.png b/emulator/skins/WVGA800/select.png
new file mode 100644 (file)
index 0000000..f4a65d3
Binary files /dev/null and b/emulator/skins/WVGA800/select.png differ
diff --git a/emulator/skins/WVGA800/spacebar.png b/emulator/skins/WVGA800/spacebar.png
new file mode 100644 (file)
index 0000000..aa459bd
Binary files /dev/null and b/emulator/skins/WVGA800/spacebar.png differ
diff --git a/emulator/skins/WVGA854/arrow_down.png b/emulator/skins/WVGA854/arrow_down.png
new file mode 100644 (file)
index 0000000..b9fde22
Binary files /dev/null and b/emulator/skins/WVGA854/arrow_down.png differ
diff --git a/emulator/skins/WVGA854/arrow_left.png b/emulator/skins/WVGA854/arrow_left.png
new file mode 100644 (file)
index 0000000..281b192
Binary files /dev/null and b/emulator/skins/WVGA854/arrow_left.png differ
diff --git a/emulator/skins/WVGA854/arrow_right.png b/emulator/skins/WVGA854/arrow_right.png
new file mode 100644 (file)
index 0000000..4cbc65d
Binary files /dev/null and b/emulator/skins/WVGA854/arrow_right.png differ
diff --git a/emulator/skins/WVGA854/arrow_up.png b/emulator/skins/WVGA854/arrow_up.png
new file mode 100644 (file)
index 0000000..29c7121
Binary files /dev/null and b/emulator/skins/WVGA854/arrow_up.png differ
diff --git a/emulator/skins/WVGA854/background_land.png b/emulator/skins/WVGA854/background_land.png
new file mode 100644 (file)
index 0000000..4f0f90e
Binary files /dev/null and b/emulator/skins/WVGA854/background_land.png differ
diff --git a/emulator/skins/WVGA854/background_port.png b/emulator/skins/WVGA854/background_port.png
new file mode 100644 (file)
index 0000000..e35bf67
Binary files /dev/null and b/emulator/skins/WVGA854/background_port.png differ
diff --git a/emulator/skins/WVGA854/button.png b/emulator/skins/WVGA854/button.png
new file mode 100644 (file)
index 0000000..8281d20
Binary files /dev/null and b/emulator/skins/WVGA854/button.png differ
diff --git a/emulator/skins/WVGA854/controls.png b/emulator/skins/WVGA854/controls.png
new file mode 100644 (file)
index 0000000..04b85e2
Binary files /dev/null and b/emulator/skins/WVGA854/controls.png differ
diff --git a/emulator/skins/WVGA854/hardware.ini b/emulator/skins/WVGA854/hardware.ini
new file mode 100644 (file)
index 0000000..02e9d89
--- /dev/null
@@ -0,0 +1,2 @@
+# skin-specific hardware values
+hw.lcd.density=240
\ No newline at end of file
diff --git a/emulator/skins/WVGA854/key.png b/emulator/skins/WVGA854/key.png
new file mode 100644 (file)
index 0000000..40b03bf
Binary files /dev/null and b/emulator/skins/WVGA854/key.png differ
diff --git a/emulator/skins/WVGA854/keyboard.png b/emulator/skins/WVGA854/keyboard.png
new file mode 100644 (file)
index 0000000..ca49dcf
Binary files /dev/null and b/emulator/skins/WVGA854/keyboard.png differ
diff --git a/emulator/skins/WVGA854/layout b/emulator/skins/WVGA854/layout
new file mode 100644 (file)
index 0000000..ab0784d
--- /dev/null
@@ -0,0 +1,437 @@
+parts {
+    portrait {
+        background {
+            image   background_port.png
+        }
+    }
+    landscape {
+        background {
+            image   background_land.png
+        }
+    }
+
+    device {
+        display {
+            width   480
+            height  854
+            x       0
+            y       0
+        }
+    }
+    
+    controls {
+        background {
+            image   controls.png
+        }
+        buttons {
+            soft-left {
+                    image button.png
+                    x 56
+                    y 142
+            }
+            home {
+                    image button.png
+                    x 0
+                    y 142
+            }
+            back {
+                    image button.png
+                    x 112
+                    y 142
+            }
+            dpad-up {
+                    image arrow_up.png
+                    x 77
+                    y 53
+            }
+            dpad-down {
+                    image arrow_down.png
+                    x 77
+                    y 106
+            }
+            dpad-left {
+                    image arrow_left.png
+                    x 53
+                    y 53
+            }
+            dpad-right {
+                    image arrow_right.png
+                    x 123
+                    y 53
+            }
+            dpad-center {
+                    image select.png
+                    x 77
+                    y 81
+            }
+            phone-dial {
+                    image button.png
+                    x 0
+                    y 71
+            }
+            phone-hangup {
+                    image button.png
+                    x 168
+                    y 71
+            }
+
+            power {
+                    image button.png
+                    x 168
+                    y 0
+            }
+
+            volume-up {
+                    image button.png
+                    x 112
+                    y 0
+            }
+
+            volume-down {
+                    image button.png
+                    x 56
+                    y 0
+            }
+
+            search {
+                    image button.png
+                    x 168
+                    y 142
+            }
+
+        }
+    }
+
+    keyboard {
+        background {
+            image   keyboard.png
+        }
+        buttons {
+            1 {
+                image  key.png
+                x  5
+                y  5
+            }
+            2 {
+                image  key.png
+                x 42
+                y 5
+            }
+            3 {
+                image  key.png
+                x 79
+                y 5
+            }
+            4 {
+                image  key.png
+                x 116
+                y 5
+            }
+            5 {
+                image  key.png
+                x 153
+                y 5
+            }
+            6 {
+                image  key.png
+                x 190
+                y 5
+            }
+            7 {
+                image  key.png
+                x 227
+                y 5
+            }
+            8 {
+                image  key.png
+                x 264
+                y 5
+            }
+            9 {
+                image  key.png
+                x 301
+                y 5
+            }
+            0 {
+                image  key.png
+                x 338
+                y 5
+            }
+
+            q {
+                image  key.png
+                x  5
+                y  41
+            }
+            w {
+                image  key.png
+                x 42
+                y 41
+            }
+            e {
+                image  key.png
+                x 79
+                y 41
+            }
+            r {
+                image  key.png
+                x 116
+                y 41
+            }
+            t {
+                image  key.png
+                x 153
+                y 41
+            }
+            y {
+                image  key.png
+                x 190
+                y 41
+            }
+            u {
+                image  key.png
+                x 227
+                y 41
+            }
+            i {
+                image  key.png
+                x 264
+                y 41
+            }
+            o {
+                image  key.png
+                x 301
+                y 41
+            }
+            p {
+                image  key.png
+                x 338
+                y 41
+            }
+
+            a {
+                image  key.png
+                x  5
+                y 77
+            }
+            s {
+                image  key.png
+                x 42
+                y 77
+            }
+            d {
+                image  key.png
+                x 79
+                y 77
+            }
+            f {
+                image  key.png
+                x 116
+                y 77
+            }
+            g {
+                image  key.png
+                x 153
+                y 77
+            }
+            h {
+                image  key.png
+                x 190
+                y 77
+            }
+            j {
+                image  key.png
+                x 227
+                y 77
+            }
+            k {
+                image  key.png
+                x 264
+                y 77
+            }
+            l {
+                image  key.png
+                x 301
+                y 77
+            }
+            DEL {
+                image  key.png
+                x 338
+                y 77
+            }
+
+            CAP {
+                image  key.png
+                x  5
+                y 113
+            }
+            z {
+                image  key.png
+                x 42
+                y 113
+            }
+            x {
+                image  key.png
+                x 79
+                y 113
+            }
+            c {
+                image  key.png
+                x 116
+                y 113
+            }
+            v {
+                image  key.png
+                x 153
+                y 113
+            }
+            b {
+                image  key.png
+                x 190
+                y 113
+            }
+            n {
+                image  key.png
+                x 227
+                y 113
+            }
+            m {
+                image  key.png
+                x 264
+                y 113
+            }
+            PERIOD {
+                image  key.png
+                x 301
+                y 113
+            }
+            ENTER {
+                image  key.png
+                x 338
+                y 113
+            }
+
+            ALT {
+                image  key.png
+                x  5
+                y 149
+            }
+            SYM {
+                image  key.png
+                x 42
+                y 149
+            }
+            AT {
+                image  key.png
+                x 79
+                y 149
+            }
+            SPACE {
+                image  spacebar.png
+                x 116
+                y 149
+            }
+            SLASH {
+                image  key.png
+                x 264
+                y 149
+            }
+            COMMA {
+                image  key.png
+                x 301
+                y 149
+            }
+            ALT2 {
+                image  key.png
+                x 338
+                y 149
+            }
+
+        }
+    }
+}
+
+layouts {
+    portrait {
+        width     950
+        height    908
+        color     0xe0e0e0
+        event     EV_SW:0:1
+        
+        part1 {
+            name    portrait
+            x       0
+            y       0
+        }
+
+        part2 {
+            name    landscape
+            x       1000
+            y       0
+        }
+
+        part3 {
+            name    device
+            x       27
+            y       27
+        }
+        part4 {
+            name    controls
+            x       635
+            y       234
+        }
+        part5 {
+            name    keyboard
+            x       554
+            y       486
+        }
+
+    }
+
+    landscape {
+        width     907
+        height    761
+        color     0xe0e0e0
+        event     EV_SW:0:0
+
+        part1 {
+            name    portrait
+            x       900
+            y       0
+        }
+
+        part2 {
+            name    landscape
+            x       0
+            y       0
+        }
+
+        part3 {
+            name      device
+            x         26
+            y         509
+            rotation  3
+        }
+
+        part4 {
+            name     controls
+            x        567
+            y        556
+        }
+
+        part5 {
+            name     keyboard
+            x        126
+            y        556
+        }
+    }
+}
+
+keyboard {
+    charmap qwerty2
+}
+
+network {
+    speed  full
+    delay  none
+}
diff --git a/emulator/skins/WVGA854/select.png b/emulator/skins/WVGA854/select.png
new file mode 100644 (file)
index 0000000..f4a65d3
Binary files /dev/null and b/emulator/skins/WVGA854/select.png differ
diff --git a/emulator/skins/WVGA854/spacebar.png b/emulator/skins/WVGA854/spacebar.png
new file mode 100644 (file)
index 0000000..aa459bd
Binary files /dev/null and b/emulator/skins/WVGA854/spacebar.png differ
index dc7a154..434a0fa 100755 (executable)
@@ -1,9 +1,12 @@
 *.sln\r
 *.vcproj*\r
+*.aps\r
 usb/api.*\r
 usb/Debug\r
 usb/Release\r
 usb/api/obj*\r
 usb/api/*.log\r
 usb/adb_winapi_test/obj*\r
-usb/adb_winapi_test/*.log
\ No newline at end of file
+usb/adb_winapi_test/*.log\r
+usb/winusb/obj*\r
+usb/winusb/*.log
\ No newline at end of file
index 1fcfaa9..b5586eb 100755 (executable)
Binary files a/host/windows/prebuilt/usb/AdbWinApi.dll and b/host/windows/prebuilt/usb/AdbWinApi.dll differ
diff --git a/host/windows/prebuilt/usb/AdbWinUsbApi.dll b/host/windows/prebuilt/usb/AdbWinUsbApi.dll
new file mode 100755 (executable)
index 0000000..0c9e00b
Binary files /dev/null and b/host/windows/prebuilt/usb/AdbWinUsbApi.dll differ
index 1806101..9cf4e61 100644 (file)
@@ -6,7 +6,8 @@ LOCAL_PREBUILT_LIBS := \
        AdbWinApi.a
 
 LOCAL_PREBUILT_EXECUTABLES := \
-       AdbWinApi.dll
+       AdbWinApi.dll  \
+       AdbWinUsbApi.dll
        
 .PHONY : kill-adb
        
index c399612..de4f93e 100755 (executable)
@@ -1,20 +1,19 @@
 ;\r
-; WinUsb installation File.\r
+; Android WinUsb driver installation.\r
 ;\r
 [Version]\r
 Signature           = "$Windows NT$"\r
 Class               = AndroidUsbDeviceClass
 ClassGuid           = {3F966BD9-FA04-4ec5-991C-D326973B5128}
 Provider            = %ProviderName%\r
-DriverVer           = 07/20/2009,2.0.0010.00001
+DriverVer           = 08/11/2009,2.0.0010.00002
 CatalogFile.NTx86   = androidwinusb86.cat
 CatalogFile.NTamd64 = androidwinusba64.cat
 \r
 ;\r
-; This section is required even though we report our driver\r
-; as a standard USB driver. If this section is removed the\r
-; installer will report an error "Required section not found\r
-; in INF file.\r
+; This section seems to be required for WinUsb driver installation.\r
+; If this section is removed the installer will report an error\r
+; "Required section not found in INF file".\r
 ;\r
 [ClassInstall32]\r
 Addreg = AndroidWinUsbClassReg\r
@@ -24,31 +23,31 @@ HKR,,,0,%ClassName%
 HKR,,Icon,,-1\r
 \r
 [Manufacturer]\r
-%ProviderName% = Google,NTx86,NTamd64\r
+%ProviderName% = Google, NTx86, NTamd64\r
 \r
 [Google.NTx86]\r
 ; HTC Dream\r
-%USB\HCT_Dream.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C01\r
-%USB\HCT_Dream_Composite.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C02&MI_01\r
-%USB\HCT_BootLoader.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0FFF\r
-; HCT Magic\r
-%USB\HCT_Magic_Composite.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01\r
+%SingleAdbInterface%        = USB_Install, USB\VID_0BB4&PID_0C01\r
+%CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C02&MI_01\r
+%SingleBootLoaderInterface% = USB_Install, USB\VID_0BB4&PID_0FFF\r
+; HTC Magic\r
+%CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C03&MI_01\r
 \r
 [Google.NTamd64]\r
 ; HTC Dream\r
-%USB\HCT_Dream.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C01\r
-%USB\HCT_Dream_Composite.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C02&MI_01\r
-%USB\HCT_BootLoader.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0FFF\r
-; HCT Magic\r
-%USB\HCT_Magic_Composite.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01\r
+%SingleAdbInterface%        = USB_Install, USB\VID_0BB4&PID_0C01\r
+%CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C02&MI_01\r
+%SingleBootLoaderInterface% = USB_Install, USB\VID_0BB4&PID_0FFF\r
+; HTC Magic\r
+%CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C03&MI_01\r
 \r
 [USB_Install]\r
-Include=winusb.inf\r
-Needs=WINUSB.NT\r
+Include = winusb.inf\r
+Needs   = WINUSB.NT\r
 \r
 [USB_Install.Services]\r
-Include=winusb.inf\r
-AddService=WinUSB,0x00000002,WinUSB_ServiceInstall\r
+Include     = winusb.inf\r
+AddService  = WinUSB,0x00000002,WinUSB_ServiceInstall\r
 \r
 [WinUSB_ServiceInstall]\r
 DisplayName     = %WinUSB_SvcDesc%\r
@@ -58,19 +57,20 @@ ErrorControl    = 1
 ServiceBinary   = %12%\WinUSB.sys\r
 \r
 [USB_Install.Wdf]\r
-KmdfService=WINUSB, WinUSB_Install\r
+KmdfService = WINUSB, WinUSB_Install\r
+\r
 [WinUSB_Install]\r
-KmdfLibraryVersion=1.7\r
+KmdfLibraryVersion  = 1.7\r
 \r
 [USB_Install.HW]\r
-AddReg=Dev_AddReg\r
+AddReg  = Dev_AddReg\r
 \r
 [Dev_AddReg]\r
 HKR,,DeviceInterfaceGUIDs,0x10000,"{F72FE0D4-CBCB-407d-8814-9ED673D0DD6B}"\r
 \r
 [USB_Install.CoInstallers]\r
-AddReg=CoInstallers_AddReg\r
-CopyFiles=CoInstallers_CopyFiles\r
+AddReg    = CoInstallers_AddReg\r
+CopyFiles = CoInstallers_CopyFiles\r
 \r
 [CoInstallers_AddReg]\r
 HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01007.dll,WdfCoInstaller","WinUSBCoInstaller.dll"\r
@@ -87,19 +87,18 @@ CoInstallers_CopyFiles=11
 2 = %DISK_NAME%,,,\amd64\r
 \r
 [SourceDisksFiles.x86]\r
-WinUSBCoInstaller.dll=1\r
-WdfCoInstaller01007.dll=1\r
+WinUSBCoInstaller.dll  = 1\r
+WdfCoInstaller01007.dll = 1\r
 \r
 [SourceDisksFiles.amd64]\r
-WinUSBCoInstaller.dll=2\r
-WdfCoInstaller01007.dll=2\r
+WinUSBCoInstaller.dll  = 2\r
+WdfCoInstaller01007.dll = 2\r
 \r
 [Strings]\r
-ProviderName="Google, Inc."\r
-USB\HCT_Dream.DeviceDesc="Android ADB Interface"\r
-USB\HCT_Dream_Composite.DeviceDesc="Android Composite ADB Interface"\r
-USB\HCT_BootLoader.DeviceDesc="Android Bootloader Interface"\r
-USB\HCT_Magic_Composite.DeviceDesc="Android Composite ADB Interface"\r
-WinUSB_SvcDesc="Android USB Driver"\r
-DISK_NAME="Android install disk"\r
-ClassName="Android Phone"\r
+ProviderName                = "Google, Inc."\r
+SingleAdbInterface          = "Android ADB Interface"\r
+CompositeAdbInterface       = "Android Composite ADB Interface"\r
+SingleBootLoaderInterface   = "Android Bootloader Interface"\r
+WinUSB_SvcDesc              = "Android USB Driver"\r
+DISK_NAME                   = "Android WinUsb installation disk"\r
+ClassName                   = "Android Phone"\r
index 4d18d37..507a2b5 100644 (file)
@@ -17,6 +17,8 @@
 // AdbWinApi.cpp : Implementation of DLL Exports.\r
 \r
 #include "stdafx.h"\r
+#include "adb_api.h"\r
+#include "adb_winusb_api.h"\r
 \r
 extern "C" {\r
 int _forceCRTManifest;\r
@@ -24,8 +26,73 @@ int _forceMFCManifest;
 int _forceAtlDllManifest;\r
 };\r
 \r
+/// References InstantiateWinUsbInterface declared in adb_api.cpp\r
+extern PFN_INSTWINUSBINTERFACE InstantiateWinUsbInterface;\r
+\r
 class CAdbWinApiModule : public CAtlDllModuleT< CAdbWinApiModule > {\r
-public:\r
+ public:\r
+  CAdbWinApiModule()\r
+      : CAtlDllModuleT< CAdbWinApiModule >(),\r
+        adbwinusbapi_handle_(NULL),\r
+        is_initialized_(false) {\r
+  }\r
+\r
+  ~CAdbWinApiModule() {\r
+    // Unload AdbWinUsbApi.dll before we exit\r
+    if (NULL != adbwinusbapi_handle_) {\r
+      FreeLibrary(adbwinusbapi_handle_);\r
+    }\r
+  }\r
+\r
+  /** \brief Loads AdbWinUsbApi.dll and caches its InstantiateWinUsbInterface\r
+    export.\r
+\r
+    This method is called from DllMain on DLL_PROCESS_ATTACH event. In this\r
+    method we will check if WINUSB.DLL required by AdbWinUsbApi.dll is\r
+    installed, and if it is we will load AdbWinUsbApi.dll and cache address of\r
+    InstantiateWinUsbInterface routine exported from AdbWinUsbApi.dll\r
+  */\r
+  void AttachToAdbWinUsbApi() {\r
+    // We only need to run this only once.\r
+    if (is_initialized_) {\r
+      return;\r
+    }\r
+\r
+    // Just mark that we have ran initialization.\r
+    is_initialized_ = true;\r
+\r
+    // Before we can load AdbWinUsbApi.dll we must make sure that WINUSB.DLL\r
+    // has been installed. Build path to the file.\r
+    wchar_t path_to_winusb_dll[MAX_PATH+1];\r
+    if (!GetSystemDirectory(path_to_winusb_dll, MAX_PATH)) {\r
+      return;\r
+    }\r
+    wcscat(path_to_winusb_dll, L"\\WINUSB.DLL");\r
+\r
+    if (0xFFFFFFFF == GetFileAttributes(path_to_winusb_dll)) {\r
+      // WINUSB.DLL is not installed. We don't (in fact, can't) load\r
+      // AdbWinUsbApi.dll\r
+      return;\r
+    }\r
+\r
+    // WINUSB.DLL is installed. Lets load AdbWinUsbApi.dll and cache its\r
+    // InstantiateWinUsbInterface export.\r
+    // We require that AdbWinUsbApi.dll is located in the same folder\r
+    // where AdbWinApi.dll and adb.exe are located, so by Windows\r
+    // conventions we can pass just module name, and not the full path.\r
+    adbwinusbapi_handle_ = LoadLibrary(L"AdbWinUsbApi.dll");\r
+    if (NULL != adbwinusbapi_handle_) {\r
+      InstantiateWinUsbInterface = reinterpret_cast<PFN_INSTWINUSBINTERFACE>\r
+          (GetProcAddress(adbwinusbapi_handle_, "InstantiateWinUsbInterface"));\r
+    }\r
+  }\r
+\r
+ protected:\r
+  /// Handle to the loaded AdbWinUsbApi.dll\r
+  HINSTANCE adbwinusbapi_handle_;\r
+\r
+  /// Flags whether or not this module has been initialized.\r
+  bool      is_initialized_;\r
 };\r
 \r
 CAdbWinApiModule _AtlModule;\r
@@ -34,5 +101,12 @@ CAdbWinApiModule _AtlModule;
 extern "C" BOOL WINAPI DllMain(HINSTANCE instance,\r
                                DWORD reason,\r
                                LPVOID reserved) {\r
-    return _AtlModule.DllMain(reason, reserved); \r
+  // Lets see if we need to initialize InstantiateWinUsbInterface\r
+  // variable. We do that only once, on condition that this DLL is\r
+  // being attached to the process and InstantiateWinUsbInterface\r
+  // address has not been calculated yet.\r
+  if (DLL_PROCESS_ATTACH == reason) {\r
+    _AtlModule.AttachToAdbWinUsbApi();\r
+  }\r
+  return _AtlModule.DllMain(reason, reserved);\r
 }\r
index f6e6614..3569521 100755 (executable)
@@ -50,8 +50,7 @@ TARGETLIBS = $(SDK_LIB_PATH)\ole32.lib    \
              $(SDK_LIB_PATH)\wbemuuid.lib \\r
              $(SDK_LIB_PATH)\uuid.lib     \\r
              $(SDK_LIB_PATH)\setupapi.lib \\r
-             $(SDK_LIB_PATH)\usbd.lib     \\r
-             $(SDK_LIB_PATH)\winusb.lib\r
+             $(SDK_LIB_PATH)\usbd.lib\r
            \r
 !IF "$(DDKBUILDENV)" == "fre"
 # Libraries for release (free) builds
@@ -87,15 +86,12 @@ PRECOMPILED_SOURCEFILE = stdafx.cpp
 # Define source files for AdbWinApi.dll\r
 SOURCES = adb_api.cpp                     \\r
           adb_endpoint_object.cpp         \\r
-          adb_winusb_endpoint_object.cpp  \\r
           adb_legacy_endpoint_object.cpp  \\r
           adb_helper_routines.cpp         \\r
           adb_interface.cpp               \\r
-          adb_winusb_interface.cpp        \\r
           adb_legacy_interface.cpp        \\r
           adb_interface_enum.cpp          \\r
           adb_io_completion.cpp           \\r
-          adb_winusb_io_completion.cpp    \\r
           adb_legacy_io_completion.cpp    \\r
           adb_object_handle.cpp           \\r
           AdbWinApi.cpp                   \\r
index f9bd94e..e58bcf1 100644 (file)
 #include "adb_object_handle.h"\r
 #include "adb_interface_enum.h"\r
 #include "adb_interface.h"\r
-#include "adb_winusb_interface.h"\r
 #include "adb_legacy_interface.h"\r
 #include "adb_endpoint_object.h"\r
 #include "adb_io_completion.h"\r
 #include "adb_helper_routines.h"\r
+#include "adb_winusb_api.h"\r
+\r
+/** \brief Points to InstantiateWinUsbInterface exported from AdbWinUsbApi.dll.\r
+\r
+  This variable is initialized with the actual address in DllMain routine for\r
+  this DLL on DLL_PROCESS_ATTACH event.\r
+  @see PFN_INSTWINUSBINTERFACE for more information.\r
+*/\r
+PFN_INSTWINUSBINTERFACE InstantiateWinUsbInterface = NULL;\r
 \r
 ADBAPIHANDLE __cdecl AdbEnumInterfaces(GUID class_id,\r
                                bool exclude_not_present,\r
@@ -101,11 +109,22 @@ ADBAPIHANDLE __cdecl AdbCreateInterfaceByName(
   ADBAPIHANDLE ret = NULL;\r
 \r
   try {\r
-    // Instantiate object\r
+    // Instantiate interface object, depending on the USB driver type.\r
     if (IsLegacyInterface(interface_name)) {\r
+      // We have legacy USB driver underneath us.\r
       obj = new AdbLegacyInterfaceObject(interface_name);\r
     } else {\r
-      obj = new AdbWinUsbInterfaceObject(interface_name);\r
+      // We have WinUsb driver underneath us. Make sure that AdbWinUsbApi.dll\r
+      // is loaded and its InstantiateWinUsbInterface routine address has\r
+      // been cached.\r
+      if (NULL != InstantiateWinUsbInterface) {\r
+        obj = InstantiateWinUsbInterface(interface_name);\r
+        if (NULL == obj) {\r
+          return NULL;\r
+        }\r
+      } else {\r
+        return NULL;\r
+      }\r
     }\r
 \r
     // Create handle for it\r
index e2ad129..20f0cd6 100644 (file)
@@ -119,8 +119,10 @@ typedef struct _AdbEndpointInformation {
 // as being exported.\r
 #ifdef ADBWIN_EXPORTS\r
 #define ADBWIN_API EXTERN_C __declspec(dllexport)\r
+#define ADBWIN_API_CLASS     __declspec(dllexport)\r
 #else\r
 #define ADBWIN_API EXTERN_C __declspec(dllimport)\r
+#define ADBWIN_API_CLASS     __declspec(dllimport)\r
 #endif\r
 \r
 /** \brief Handle to an API object.\r
index 295eb46..d92aaad 100644 (file)
@@ -29,7 +29,7 @@
   This class implement functionality that is common for both, WinUsb and\r
   legacy APIs.\r
 */\r
-class AdbEndpointObject : public AdbObjectHandle {\r
+class ADBWIN_API_CLASS AdbEndpointObject : public AdbObjectHandle {\r
  public:\r
   /** \brief Constructs the object\r
     \r
index 4afb17d..0aa0d1d 100644 (file)
 \r
 #include "adb_object_handle.h"\r
 \r
+// 'AdbInterfaceObject::interface_name_' : class 'std::basic_string<_E,_Tr,_A>'\r
+// needs to have dll-interface to be used by clients of class\r
+// 'AdbInterfaceObject' We're ok with that, since interface_name_ will not\r
+// be referenced by name from outside of this class.\r
+#pragma warning(disable: 4251)\r
 /** \brief Encapsulates an interface on our USB device.\r
 \r
   This is an abstract class that implements functionality common for both,\r
   legacy, and WinUsb based interfaces.\r
 */\r
-class AdbInterfaceObject : public AdbObjectHandle {\r
+class ADBWIN_API_CLASS AdbInterfaceObject : public AdbObjectHandle {\r
  public:\r
   /** \brief Constructs the object.\r
     \r
@@ -180,9 +185,6 @@ class AdbInterfaceObject : public AdbObjectHandle {
   }\r
 \r
  protected:\r
-  /// Name of the USB interface (device name) for this object\r
-  std::wstring                  interface_name_;\r
-\r
   /// Cached usb device descriptor\r
   USB_DEVICE_DESCRIPTOR         usb_device_descriptor_;\r
 \r
@@ -191,6 +193,11 @@ class AdbInterfaceObject : public AdbObjectHandle {
 \r
   /// Cached usb interface descriptor\r
   USB_INTERFACE_DESCRIPTOR      usb_interface_descriptor_;\r
+\r
+ private:\r
+  /// Name of the USB interface (device name) for this object\r
+  std::wstring                  interface_name_;\r
 };\r
+#pragma warning(default: 4251)\r
 \r
 #endif  // ANDROID_USB_API_ADB_INTERFACE_H__\r
index 8a7c1d9..ea4b4fb 100644 (file)
@@ -33,7 +33,7 @@
   like all other handles this handle must be closed after it's no longer\r
   needed.\r
 */\r
-class AdbIOCompletion : public AdbObjectHandle {\r
+class ADBWIN_API_CLASS AdbIOCompletion : public AdbObjectHandle {\r
  public:\r
   /** \brief Constructs the object\r
     \r
index 29ac5e2..2fa4ad0 100644 (file)
@@ -22,6 +22,7 @@
   of the API through a handle.\r
 */\r
 \r
+#include "adb_api.h"\r
 #include "adb_api_private_defines.h"\r
 \r
 /** \brief Defines types of internal API objects\r
@@ -71,7 +72,7 @@ enum AdbObjectType {
   All API objects that have handles that are sent back to API client must be\r
   derived from this class.\r
 */\r
-class AdbObjectHandle {\r
+class ADBWIN_API_CLASS AdbObjectHandle {\r
  public:\r
   /** \brief Constructs the object\r
 \r
diff --git a/host/windows/usb/api/adb_winusb_api.h b/host/windows/usb/api/adb_winusb_api.h
new file mode 100755 (executable)
index 0000000..268a998
--- /dev/null
@@ -0,0 +1,48 @@
+/*\r
+ * Copyright (C) 2006 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+#ifndef ANDROID_USB_API_ADBWINUSBAPI_H__\r
+#define ANDROID_USB_API_ADBWINUSBAPI_H__\r
+\r
+/** \file\r
+  Contains declarations required to link AdbWinApi and AdbWinUsbApi DLLs.\r
+*/\r
+\r
+/** \brief Function prototype for InstantiateWinUsbInterface routine exported\r
+  from AdbWinUsbApi.dll\r
+\r
+  In order to provide backward compatibility with the systems that still run\r
+  legacy (custom) USB drivers, and have not installed WINUSB.DLL we need to\r
+  split functionality of our ADB API on Windows between two DLLs: AdbWinApi,\r
+  and AdbWinUsbApi. AdbWinApi is fully capable of working on top of the legacy\r
+  driver, but has no traces to WinUsb. AdbWinUsbApi is capable of working on\r
+  top of WinUsb API. We are forced to do this split, because we can have\r
+  dependency on WINUSB.DLL in the DLL that implements legacy API. The problem\r
+  is that customers may have a legacy driver that they don't want to upgrade\r
+  to WinUsb, so they may not have WINUSB.DLL installed on their machines, but\r
+  they still must be able to use ADB. So, the idea behind the split is as\r
+  such. When AdbWinApi.dll is loaded into a process, it will check WINUSB.DLL\r
+  installation (by checking existance of C:\Windows\System32\winusb.dll). If\r
+  WINUSB.DLL is installed, AdbWinApi will also load AdbWinUsbApi.dll (by\r
+  calling LoadLibrary), and will extract address of InstantiateWinUsbInterface\r
+  routine exported from AdbWinUsbApi.dll. Then this routine will be used to\r
+  instantiate AdbInterfaceObject instance on condition that it is confirmed\r
+  that USB driver underneath us is in deed WinUsb.\r
+*/\r
+typedef class AdbInterfaceObject* \\r
+    (__cdecl *PFN_INSTWINUSBINTERFACE)(const wchar_t*);\r
+\r
+#endif  // ANDROID_USB_API_ADBWINUSBAPI_H__\r
index 92b2652..d57bec7 100644 (file)
 #include <string>\r
 #pragma warning(default: 4201)\r
 #pragma warning(disable: 4200)\r
-extern "C" {\r
 #include <usbdi.h>\r
-#include <winusb.h>\r
 #include <usb100.h>\r
-}\r
 \r
 #include "resource.h"\r
 \r
diff --git a/host/windows/usb/winusb/AdbWinUsbApi.cpp b/host/windows/usb/winusb/AdbWinUsbApi.cpp
new file mode 100755 (executable)
index 0000000..4916eeb
--- /dev/null
@@ -0,0 +1,62 @@
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+// AdbWinUsbApi.cpp : Implementation of DLL Exports.\r
+\r
+#include "stdafx.h"\r
+#include "adb_winusb_interface.h"\r
+\r
+class CAdbWinApiModule : public CAtlDllModuleT< CAdbWinApiModule > {\r
+public:\r
+};\r
+\r
+CAdbWinApiModule _AtlModule;\r
+\r
+// DLL Entry Point\r
+extern "C" BOOL WINAPI DllMain(HINSTANCE instance,\r
+                               DWORD reason,\r
+                               LPVOID reserved) {\r
+    return _AtlModule.DllMain(reason, reserved);\r
+}\r
+\r
+/** \brief Instantiates interface instance that uses WinUsb API to communicate\r
+  with USB driver.\r
+\r
+  This is the only exported routine from this DLL. This routine instantiates an\r
+  object of AdbWinUsbInterfaceObject on request from AdbWinApi.dll when it is\r
+  detected that underlying USB driver is WinUsb.sys.\r
+  @param[in] interface_name Name of the interface.\r
+  @return AdbInterfaceObject - casted instance of AdbWinUsbInterfaceObject\r
+          object on success, or NULL on failure with GetLastError providing\r
+          information on an error that occurred.\r
+*/\r
+extern "C" __declspec(dllexport)\r
+AdbInterfaceObject* __cdecl InstantiateWinUsbInterface(\r
+    const wchar_t* interface_name) {\r
+    // Validate parameter.\r
+    if (NULL == interface_name) {\r
+        return NULL;\r
+    }\r
+\r
+    // Instantiate requested object.\r
+    try {\r
+        return new AdbWinUsbInterfaceObject(interface_name);\r
+    } catch (...) {\r
+        // We expect only OOM exceptions here.\r
+        SetLastError(ERROR_OUTOFMEMORY);\r
+        return NULL;\r
+    }\r
+}\r
diff --git a/host/windows/usb/winusb/AdbWinUsbApi.def b/host/windows/usb/winusb/AdbWinUsbApi.def
new file mode 100755 (executable)
index 0000000..9e616e9
--- /dev/null
@@ -0,0 +1,5 @@
+; AdbWinUsbApi.def : Declares the module parameters.\r
+\r
+LIBRARY      "AdbWinUsbApi.DLL"\r
+\r
+EXPORTS\r
diff --git a/host/windows/usb/winusb/AdbWinUsbApi.rc b/host/windows/usb/winusb/AdbWinUsbApi.rc
new file mode 100755 (executable)
index 0000000..44aa100
--- /dev/null
@@ -0,0 +1,111 @@
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+//Microsoft Visual C++ generated resource script.\r
+//\r
+#include "resource.h"\r
+\r
+#define APSTUDIO_READONLY_SYMBOLS\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// Generated from the TEXTINCLUDE 2 resource.\r
+//\r
+#include "winres.h"\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+#undef APSTUDIO_READONLY_SYMBOLS\r
+\r
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r
+LANGUAGE 9, 1\r
+#pragma code_page(1252)\r
+#ifdef APSTUDIO_INVOKED\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// TEXTINCLUDE\r
+//\r
+\r
+1 TEXTINCLUDE\r
+BEGIN\r
+    "resource.h\0"\r
+END\r
+\r
+2 TEXTINCLUDE\r
+BEGIN\r
+    "#include ""winres.h""\r\n"\r
+    "\0"\r
+END\r
+\r
+#endif    // APSTUDIO_INVOKED\r
+\r
+#ifndef _MAC\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// Version\r
+//\r
+\r
+VS_VERSION_INFO VERSIONINFO\r
+ FILEVERSION 2,0,0,0\r
+ PRODUCTVERSION 2,0,0,0\r
+ FILEFLAGSMASK 0x3fL\r
+#ifdef _DEBUG\r
+ FILEFLAGS 0x1L\r
+#else\r
+ FILEFLAGS 0x0L\r
+#endif\r
+ FILEOS 0x4L\r
+ FILETYPE 0x2L\r
+ FILESUBTYPE 0x0L\r
+BEGIN\r
+    BLOCK "StringFileInfo"\r
+    BEGIN\r
+        BLOCK "040904e4"\r
+        BEGIN\r
+            VALUE "CompanyName", "Google, inc"\r
+            VALUE "FileDescription", "Android ADB API (WinUsb)"\r
+            VALUE "FileVersion", "2.0.0.0"\r
+            VALUE "LegalCopyright", "Copyright (C) 2006 The Android Open Source Project"\r
+            VALUE "InternalName", "AdbWinUsbApi.dll"\r
+            VALUE "OriginalFilename", "AdbWinUsbApi.dll"\r
+            VALUE "ProductName", "Android SDK"\r
+            VALUE "ProductVersion", "2.0.0.0"\r
+            VALUE "OLESelfRegister", ""\r
+        END\r
+    END\r
+    BLOCK "VarFileInfo"\r
+    BEGIN\r
+               VALUE "Translation", 0x0409, 1252\r
+    END\r
+END\r
+\r
+#endif    // !_MAC\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// String Table\r
+//\r
+\r
+STRINGTABLE\r
+BEGIN\r
+       IDS_PROJNAME                                    "AdbWinUsbApi"\r
+END\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+\r
+\r
+#endif\r
+\r
+#ifndef APSTUDIO_INVOKED\r
+#endif    // not APSTUDIO_INVOKED\r
diff --git a/host/windows/usb/winusb/BUILDME.TXT b/host/windows/usb/winusb/BUILDME.TXT
new file mode 100755 (executable)
index 0000000..2a459ef
--- /dev/null
@@ -0,0 +1,7 @@
+In order to build AdbWinUsbApi.dll you will need to install Windows Driver Kit,\r
+which can be obtained from Microsoft. Assuming that WDK is installed, you\r
+need to set one of the WDK's build environments, "cd" back into this directory,\r
+and execute "build -cbeEIFZ" to clean and rebuild this project, or you can\r
+execute "build -befEIF" to do a minimal build.\r
+Note that you need to build AdbWinApi.dll (..\api) before you build\r
+AdbWinUsbApi.dll, as it depends on AdbWinApi.lib library.\r
diff --git a/host/windows/usb/winusb/MAKEFILE b/host/windows/usb/winusb/MAKEFILE
new file mode 100755 (executable)
index 0000000..fcd896d
--- /dev/null
@@ -0,0 +1,22 @@
+#\r
+#  Copyright (C) 2009 The Android Open Source Project\r
+# \r
+#  Licensed under the Apache License, Version 2.0 (the "License");\r
+#  you may not use this file except in compliance with the License.\r
+#  You may obtain a copy of the License at\r
+# \r
+#       http://www.apache.org/licenses/LICENSE-2.0\r
+# \r
+#  Unless required by applicable law or agreed to in writing, software\r
+#  distributed under the License is distributed on an "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+#  See the License for the specific language governing permissions and\r
+#  limitations under the License.\r
+#\r
+\r
+#\r
+# DO NOT EDIT THIS FILE!!!  Edit .\sources. if you want to add a new source\r
+# file to this component.  This file merely indirects to the real make file\r
+# that is shared by all the components of NT OS/2\r
+#\r
+!INCLUDE $(NTMAKEENV)\makefile.def\r
diff --git a/host/windows/usb/winusb/Resource.h b/host/windows/usb/winusb/Resource.h
new file mode 100755 (executable)
index 0000000..3ede761
--- /dev/null
@@ -0,0 +1,34 @@
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+//{{NO_DEPENDENCIES}}\r
+// Microsoft Visual C++ generated include file.\r
+// Used by AdbWinApi.rc\r
+//\r
+\r
+#define IDS_PROJNAME                    100\r
+#define IDR_ADBWINAPI  101\r
+\r
+// Next default values for new objects\r
+//\r
+#ifdef APSTUDIO_INVOKED\r
+#ifndef APSTUDIO_READONLY_SYMBOLS\r
+#define _APS_NEXT_RESOURCE_VALUE        201\r
+#define _APS_NEXT_COMMAND_VALUE         32768\r
+#define _APS_NEXT_CONTROL_VALUE         201\r
+#define _APS_NEXT_SYMED_VALUE           102\r
+#endif\r
+#endif\r
diff --git a/host/windows/usb/winusb/SOURCES b/host/windows/usb/winusb/SOURCES
new file mode 100755 (executable)
index 0000000..80d17ae
--- /dev/null
@@ -0,0 +1,93 @@
+#\r
+#  Copyright (C) 2009 The Android Open Source Project\r
+# \r
+#  Licensed under the Apache License, Version 2.0 (the "License");\r
+#  you may not use this file except in compliance with the License.\r
+#  You may obtain a copy of the License at\r
+# \r
+#       http://www.apache.org/licenses/LICENSE-2.0\r
+# \r
+#  Unless required by applicable law or agreed to in writing, software\r
+#  distributed under the License is distributed on an "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+#  See the License for the specific language governing permissions and\r
+#  limitations under the License.\r
+#\r
+\r
+TARGETNAME = AdbWinUsbApi\r
+TARGETPATH = obj\r
+TARGETTYPE = DYNLINK\r
+\r
+UMTYPE = windows\r
+DLLDEF = AdbWinUsbApi.def\r
+\r
+# Use statically linked atl libraries:\r
+# - atls.lib for free build\r
+# - atlsd.lib for checked build\r
+USE_STATIC_ATL  = 1\r
+# Use ATL v. 7.1\r
+ATL_VER         = 71\r
+# Use STL v. 6.0\r
+USE_STL         = 1\r
+STL_VER         = 60\r
+# Use multithreaded libraries\r
+USE_LIBCMT      = 1\r
+\r
+# Include directories\r
+INCLUDES = $(DDK_INC_PATH);           \\r
+           $(SDK_INC_PATH);           \\r
+           $(CRT_INC_PATH);           \\r
+           $(SDK_INC_PATH)\crt;       \\r
+           $(CRT_INC_PATH)\atl71;     \\r
+           $(SDK_INC_PATH)\crt\stl60\r
+\r
+# Common target libraries\r
+TARGETLIBS = $(SDK_LIB_PATH)\ole32.lib    \\r
+             $(SDK_LIB_PATH)\Advapi32.lib \\r
+             $(SDK_LIB_PATH)\Kernel32.lib \\r
+             $(SDK_LIB_PATH)\User32.lib   \\r
+             $(SDK_LIB_PATH)\oleaut32.lib \\r
+             $(SDK_LIB_PATH)\wbemuuid.lib \\r
+             $(SDK_LIB_PATH)\uuid.lib     \\r
+             $(SDK_LIB_PATH)\setupapi.lib \\r
+             $(SDK_LIB_PATH)\usbd.lib     \\r
+             $(SDK_LIB_PATH)\winusb.lib   \\r
+             ..\api\obj$(BUILD_ALT_DIR)\i386\AdbWinApi.lib\r
+\r
+!IF "$(DDKBUILDENV)" == "fre"
+# Libraries for release (free) builds
+TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atls.lib\r
+!ELSE\r
+# Libraries for debug (checked) builds
+TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atlsd.lib\r
+!ENDIF\r
+\r
+# Common C defines\r
+C_DEFINES= $(C_DEFINES) -DADBWINUSB_EXPORTS -D_UNICODE \\r
+           -DUNICODE -DWIN32 -D_WINDOWS -D_USRDLL -D_WINDLL\r
+\r
+!IF "$(DDKBUILDENV)" == "fre"
+# C defines for release (free) builds
+C_DEFINES = $(C_DEFINES) -DNDEBUG\r
+!ELSE\r
+# C defines for debug (checked) builds
+C_DEFINES = $(C_DEFINES) -D_DEBUG\r
+!ENDIF\r
+\r
+# Turn on all warnings, and treat warnings as errors\r
+MSC_WARNING_LEVEL = /W4 /Wp64 /WX
+
+# Common C defines\r
+USER_C_FLAGS = $(USER_C_FLAGS) /FD /EHsc /wd4100 /wd4200 /wd4702 /nologo
+
+# Set precompiled header information
+PRECOMPILED_CXX = 1\r
+PRECOMPILED_INCLUDE = stdafx.h\r
+PRECOMPILED_SOURCEFILE = stdafx.cpp\r
+\r
+# Define source files for AdbWinUsbApi.dll\r
+SOURCES = adb_winusb_endpoint_object.cpp  \\r
+          adb_winusb_interface.cpp        \\r
+          adb_winusb_io_completion.cpp    \\r
+          AdbWinUsbApi.cpp                \\r
+                     AdbWinUsbApi.rc\r
@@ -22,7 +22,6 @@
 #include "stdafx.h"\r
 #include "adb_winusb_endpoint_object.h"\r
 #include "adb_winusb_io_completion.h"\r
-#include "adb_helper_routines.h"\r
 \r
 AdbWinUsbEndpointObject::AdbWinUsbEndpointObject(\r
     AdbWinUsbInterfaceObject* parent_interf,\r
@@ -34,6 +33,17 @@ AdbWinUsbEndpointObject::AdbWinUsbEndpointObject(
 AdbWinUsbEndpointObject::~AdbWinUsbEndpointObject() {\r
 }\r
 \r
+LONG AdbWinUsbEndpointObject::Release() {\r
+  ATLASSERT(ref_count_ > 0);\r
+  LONG ret = InterlockedDecrement(&ref_count_);\r
+  ATLASSERT(ret >= 0);\r
+  if (0 == ret) {\r
+    LastReferenceReleased();\r
+    delete this;\r
+  }\r
+  return ret;\r
+}\r
+\r
 ADBAPIHANDLE AdbWinUsbEndpointObject::CommonAsyncReadWrite(\r
     bool is_read,\r
     void* buffer,\r
@@ -21,7 +21,7 @@
   encapsulates a handle opened to a WinUsb endpoint on our device.\r
 */\r
 \r
-#include "adb_endpoint_object.h"\r
+#include "..\api\adb_endpoint_object.h"\r
 #include "adb_winusb_interface.h"\r
 \r
 /** Class AdbWinUsbEndpointObject encapsulates a handle opened to an endpoint on\r
@@ -49,6 +49,30 @@ class AdbWinUsbEndpointObject : public AdbEndpointObject {
   virtual ~AdbWinUsbEndpointObject();\r
 \r
   //\r
+  // Virtual overrides\r
+  //\r
+\r
+ public:\r
+  /** \brief Releases the object.\r
+\r
+    If refcount drops to zero as the result of this release, the object is\r
+    destroyed in this method. As a general rule, objects must not be touched\r
+    after this method returns even if returned value is not zero. We override\r
+    this method in order to make sure that objects of this class are deleted\r
+    in contect of the DLL they were created in. The problem is that since\r
+    objects of this class were created in context of AdbWinUsbApi module, they\r
+    are allocated from the heap assigned to that module. Now, if these objects\r
+    are deleted outside of AdbWinUsbApi module, this will lead to the heap\r
+    corruption in the module that deleted these objects. Since all objects of\r
+    this class are deleted in the Release method only, by overriding it we make\r
+    sure that we free memory in the context of the module where it was\r
+    allocated.\r
+    @return Value of the reference counter after object is released in this\r
+            method.\r
+  */\r
+  virtual LONG Release();\r
+\r
+  //\r
   // Abstract overrides\r
   //\r
 \r
similarity index 95%
rename from host/windows/usb/api/adb_winusb_interface.cpp
rename to host/windows/usb/winusb/adb_winusb_interface.cpp
index d09c1cb..9d0377a 100755 (executable)
@@ -40,6 +40,17 @@ AdbWinUsbInterfaceObject::~AdbWinUsbInterfaceObject() {
   ATLASSERT(INVALID_HANDLE_VALUE == usb_device_handle_);\r
 }\r
 \r
+LONG AdbWinUsbInterfaceObject::Release() {\r
+  ATLASSERT(ref_count_ > 0);\r
+  LONG ret = InterlockedDecrement(&ref_count_);\r
+  ATLASSERT(ret >= 0);\r
+  if (0 == ret) {\r
+    LastReferenceReleased();\r
+    delete this;\r
+  }\r
+  return ret;\r
+}\r
+\r
 ADBAPIHANDLE AdbWinUsbInterfaceObject::CreateHandle() {\r
   // Open USB device for this inteface Note that WinUsb API\r
   // requires the handle to be opened for overlapped I/O.\r
similarity index 84%
rename from host/windows/usb/api/adb_winusb_interface.h
rename to host/windows/usb/winusb/adb_winusb_interface.h
index 82f7f89..2311fd1 100755 (executable)
@@ -22,7 +22,7 @@
   via WinUsb API.\r
 */\r
 \r
-#include "adb_interface.h"\r
+#include "..\api\adb_interface.h"\r
 \r
 /** \brief Encapsulates an interface on our USB device that is accessible\r
   via WinUsb API.\r
@@ -48,6 +48,25 @@ class AdbWinUsbInterfaceObject : public AdbInterfaceObject {
   //\r
 \r
  public:\r
+  /** \brief Releases the object.\r
+\r
+    If refcount drops to zero as the result of this release, the object is\r
+    destroyed in this method. As a general rule, objects must not be touched\r
+    after this method returns even if returned value is not zero. We override\r
+    this method in order to make sure that objects of this class are deleted\r
+    in contect of the DLL they were created in. The problem is that since\r
+    objects of this class were created in context of AdbWinUsbApi module, they\r
+    are allocated from the heap assigned to that module. Now, if these objects\r
+    are deleted outside of AdbWinUsbApi module, this will lead to the heap\r
+    corruption in the module that deleted these objects. Since all objects of\r
+    this class are deleted in the Release method only, by overriding it we make\r
+    sure that we free memory in the context of the module where it was\r
+    allocated.\r
+    @return Value of the reference counter after object is released in this\r
+            method.\r
+  */\r
+  virtual LONG Release();\r
+\r
   /** \brief Creates handle to this object.\r
 \r
     In this call a handle for this object is generated and object is added\r
@@ -33,6 +33,17 @@ AdbWinUsbIOCompletion::AdbWinUsbIOCompletion(
 AdbWinUsbIOCompletion::~AdbWinUsbIOCompletion() {\r
 }\r
 \r
+LONG AdbWinUsbIOCompletion::Release() {\r
+  ATLASSERT(ref_count_ > 0);\r
+  LONG ret = InterlockedDecrement(&ref_count_);\r
+  ATLASSERT(ret >= 0);\r
+  if (0 == ret) {\r
+    LastReferenceReleased();\r
+    delete this;\r
+  }\r
+  return ret;\r
+}\r
+\r
 bool AdbWinUsbIOCompletion::GetOvelappedIoResult(LPOVERLAPPED ovl_data,\r
                                                  ULONG* bytes_transferred,\r
                                                  bool wait) {\r
similarity index 75%
rename from host/windows/usb/api/adb_winusb_io_completion.h
rename to host/windows/usb/winusb/adb_winusb_io_completion.h
index a97a3a8..93a4c07 100755 (executable)
@@ -22,7 +22,7 @@
   asynchronous I/O requests issued via WinUsb API.\r
 */\r
 \r
-#include "adb_io_completion.h"\r
+#include "..\api\adb_io_completion.h"\r
 #include "adb_winusb_endpoint_object.h"\r
 \r
 /** \brief Encapsulates encapsulates a wrapper around OVERLAPPED Win32\r
@@ -57,6 +57,30 @@ class AdbWinUsbIOCompletion : public AdbIOCompletion {
   virtual ~AdbWinUsbIOCompletion();\r
 \r
   //\r
+  // Virtual overrides\r
+  //\r
+\r
+ public:\r
+  /** \brief Releases the object.\r
+\r
+    If refcount drops to zero as the result of this release, the object is\r
+    destroyed in this method. As a general rule, objects must not be touched\r
+    after this method returns even if returned value is not zero. We override\r
+    this method in order to make sure that objects of this class are deleted\r
+    in contect of the DLL they were created in. The problem is that since\r
+    objects of this class were created in context of AdbWinUsbApi module, they\r
+    are allocated from the heap assigned to that module. Now, if these objects\r
+    are deleted outside of AdbWinUsbApi module, this will lead to the heap\r
+    corruption in the module that deleted these objects. Since all objects of\r
+    this class are deleted in the Release method only, by overriding it we make\r
+    sure that we free memory in the context of the module where it was\r
+    allocated.\r
+    @return Value of the reference counter after object is released in this\r
+            method.\r
+  */\r
+  virtual LONG Release();\r
+\r
+  //\r
   // Abstract overrides\r
   //\r
 \r
diff --git a/host/windows/usb/winusb/stdafx.cpp b/host/windows/usb/winusb/stdafx.cpp
new file mode 100755 (executable)
index 0000000..562765b
--- /dev/null
@@ -0,0 +1,21 @@
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+// stdafx.cpp : source file that includes just the standard includes\r
+// AdbWinUsbApi.pch will be the pre-compiled header\r
+// stdafx.obj will contain the pre-compiled type information\r
+\r
+#include "stdafx.h"\r
diff --git a/host/windows/usb/winusb/stdafx.h b/host/windows/usb/winusb/stdafx.h
new file mode 100755 (executable)
index 0000000..c2aa8de
--- /dev/null
@@ -0,0 +1,78 @@
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/** \file\r
+  Visual Studio generated include file for standard system include files, or\r
+  project specific include files that are used frequently, but are changed\r
+  infrequently.\r
+*/\r
+\r
+#pragma once\r
+\r
+#ifndef STRICT\r
+#define STRICT\r
+#endif\r
+\r
+// Modify the following defines if you have to target a platform prior to the ones specified below.\r
+// Refer to MSDN for the latest info on corresponding values for different platforms.\r
+#ifndef WINVER                         // Allow use of features specific to Windows 95 and Windows NT 4 or later.\r
+#define WINVER 0x0500          // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.\r
+#endif\r
+\r
+#ifndef _WIN32_WINNT           // Allow use of features specific to Windows NT 4 or later.\r
+#define _WIN32_WINNT 0x0500    // Change this to the appropriate value to target Windows 2000 or later.\r
+#endif\r
+\r
+#ifndef _WIN32_WINDOWS         // Allow use of features specific to Windows 98 or later.\r
+#define _WIN32_WINDOWS 0x0500 // Change this to the appropriate value to target Windows Me or later.\r
+#endif\r
+\r
+#ifndef _WIN32_IE                      // Allow use of features specific to IE 4.0 or later.\r
+#define _WIN32_IE 0x0501       // Change this to the appropriate value to target IE 5.0 or later.\r
+#endif\r
+\r
+// These defines prevent the MS header files from ejecting #pragma comment\r
+// statements with the manifest information of the used ATL, STL, and CRT\r
+#define _ATL_NOFORCE_MANIFEST\r
+#define _STL_NOFORCE_MANIFEST\r
+#define _CRT_NOFORCE_MANIFEST\r
+\r
+#define _ATL_APARTMENT_THREADED\r
+#define _ATL_NO_AUTOMATIC_NAMESPACE\r
+\r
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS     // some CString constructors will be explicit\r
+\r
+// turns off ATL's hiding of some common and often safely ignored warning messages\r
+#define _ATL_ALL_WARNINGS\r
+\r
+// #define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers\r
+\r
+#include <windows.h>\r
+#pragma warning(disable: 4702)\r
+#pragma warning(disable: 4201)\r
+#include <atlbase.h>\r
+#include <winioctl.h>\r
+#include <setupapi.h>\r
+#include <vector>\r
+#include <map>\r
+#include <string>\r
+#pragma warning(default: 4201)\r
+#pragma warning(disable: 4200)\r
+#include <winusb.h>\r
+\r
+#include "resource.h"\r
+\r
+using namespace ATL;\r
index f2b9697..002150a 100755 (executable)
@@ -23,7 +23,7 @@ NDK_ROOT_DIR=`dirname $0`/../..
 NDK_ROOT_DIR=`cd $NDK_ROOT_DIR && pwd`
 
 # the release name
-RELEASE=1.5_r2
+RELEASE=1.6_r1
 
 # the package prefix
 PREFIX=android-ndk
index d7771da..faad11d 100644 (file)
@@ -1,7 +1,7 @@
 Android NDK ChangeLog:
 
 -------------------------------------------------------------------------------
-current version
+android-ndk-1.6_r1
 
 IMPORTANT BUG FIXES:
 
diff --git a/pdk/docs/community/index.jd b/pdk/docs/community/index.jd
new file mode 100644 (file)
index 0000000..bc04eee
--- /dev/null
@@ -0,0 +1,6 @@
+home=true
+@jd:body
+
+<p>
+  Some community information here
+</p>
index 14e80bb..66c05f7 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Audio
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
index e514ef0..bcf88db 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Bluetooth
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
index 86e9c93..ea46f23 100755 (executable)
@@ -1,6 +1,7 @@
 page.title=Bluetooth Process Diagram
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <p>The diagram below offers a process-oriented architectural overview of Android's Bluetooth stack. Click <a href="../bluetooth.html">Bluetooth</a> to return to the Bluetooth overview page.</p>
-<img src="images/androidBluetoothProcessDiagram.jpg">
\ No newline at end of file
+<img src="images/androidBluetoothProcessDiagram.jpg">
index 85d7b26..a11fe00 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Bring Up
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <p>Once your code is built and you have verified that all necessary directories exist, power on and test your device with basic bring up, as described below. Bring up tests are typically designed to stress certain aspects of your system and allow you to characterize the device's behavior. </p>
@@ -355,4 +356,4 @@ service akmd /sbin/akmd
   disabled
   user akmd
   group akmd
-</pre>
\ No newline at end of file
+</pre>
index fc34132..4bed947 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Build Cookbook
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
@@ -552,4 +553,4 @@ module.  This can be fixed.  If you ever need it to be, just ask.</p>
 <td valign="top"></td>
 </tr>
 
-</table>
\ No newline at end of file
+</table>
index dabdfb0..28e7c2e 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Configuring a New Product
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 
index d229983..fd0ff80 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Android Build System
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 
@@ -266,4 +267,4 @@ If you build one flavor and then want to build another, you should run
 <code>make installclean</code> between the two makes to guarantee that
 you don't pick up files installed by the previous flavor.  <code>make
 clean</code> will also suffice, but it takes a lot longer.
-</p> 
\ No newline at end of file
+</p> 
index 281f76d..98636d8 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Camera
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
index 9c39cd7..9c56681 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Customization
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
@@ -317,4 +318,4 @@ include $(LOCAL_PATH)/client/Android.mk
 <a name="androidThemesAnimations"></a><h4>Animations</h4> 
  
 <p>Android supports configurable animations for window and view transitions.  System-level animations are defined in XML in global resource files located in <code>//android/framework/base/core/res/res/anim/</code>.</p> 
\ No newline at end of file
index 948496a..6fa37da 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Dalvik
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
index 9717cf3..f2a2db5 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Debugging with GDB
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
@@ -142,4 +143,4 @@ Execute the line shown in the debug output, substituting 5039  for the proper <c
 </pre>
 <p>Otherwise you need to determine the path of the crashing binary and follow the
   steps as mentioned above (for example, <code>gdbclient hoser :5039</code> if
-  the <code>hoser</code> command has failed).</p>
\ No newline at end of file
+  the <code>hoser</code> command has failed).</p>
index 6705766..0f8b397 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Debugging Native Code
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 
index ac41c55..1eddd15 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Display Drivers
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 
index 19a7069..e735274 100755 (executable)
@@ -1,4 +1,6 @@
 page.title=Getting Source Code
+pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <a name="toc"/>
index f2ce11c..2acad6d 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=GPS
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
index 87f19d5..0423515 100755 (executable)
@@ -1,4 +1,6 @@
 page.title=Providing Heap Memory
+pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div class="navigation" id="top">
index c5a94fc..e1e942c 100755 (executable)
@@ -1,4 +1,6 @@
 page.title=Networking Support
+pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div class="navigation" id="top">
index a7dc7e8..d22fae8 100644 (file)
@@ -1,5 +1,6 @@
 page.title=Android Platform Developer's Guide
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 
index a8eab2e..f2c51ef 100755 (executable)
@@ -1,4 +1,6 @@
 page.title=Instrumentation Framework
+pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <a name="toc"/>
index 00c8248..e4d7cc5 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Instrumentation Testing
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
index d391b0f..21b34a7 100755 (executable)
@@ -1,4 +1,6 @@
 page.title=Source Code Overview
+pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <a name="toc"/>
index e53149a..5db0a86 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Keymaps and Keyboard Input
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 
@@ -500,4 +501,4 @@ I/EventHub( 1548): Reporting device opened: id=0x10000, name=/dev/input/event0
 I/KeyInputQueue( 1548): Device added: id=0x10000, name=partnerxx_keypad, classes=1
 I/KeyInputQueue( 1548):   Keymap: partnerxx_keypad.kl
 </pre>
-<p>The snippet above contains artificial line breaks to maintain a print-friendly document.</p>
\ No newline at end of file
+<p>The snippet above contains artificial line breaks to maintain a print-friendly document.</p>
index 4f19c5d..1c445b2 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Lights
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
index 91517fc..daff6ed 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Power Management
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <a name="toc"/>
index a57cb10..7e83834 100755 (executable)
@@ -1,6 +1,8 @@
 page.title=Creating Release Keys and Signing Builds
 pdk.version=1.0
+doc.type=guide
 @jd:body
+
 <div id="qv-wrapper">
   <div id ="qv"> 
 <h2>In this document</h2>
index d41dcd7..61ca2d9 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Sensors
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
index 5c0bed5..83c4a01 100755 (executable)
@@ -1,4 +1,6 @@
 page.title=Host System Setup
+pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <a name="toc"/>
index 0ce0b4a..4f68c48 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Sim Toolkit Application (STK)
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
@@ -16,7 +17,7 @@ pdk.version=1.0
 </div>
 
 
-<p>This document offers a high-level overview of the SIM Toolkit Application for Android 1.0 and is primarily of interest for implementors of the Radio Interface Layer (RIL). The STK is  R96 compatible (3GPP TS 11.14 v5.9.0) and complies partially with R99 (3GPP TS 101.267 v8.17.0). See the <a href="androidSTKFeatureList">STK Feature List</a> for the complete feature list. </p> 
+<p>This document offers a high-level overview of the SIM Toolkit Application for Android 1.0 and is primarily of interest for implementors of the Radio Interface Layer (RIL). The STK is  R96 compatible (3GPP TS 11.14 v5.9.0) and complies partially with R99 (3GPP TS 101.267 v8.17.0). See the <a href="#androidSTKFeatureList">STK Feature List</a> for the complete feature list. </p>.
 <p>The Android STK implementation includes three layers:</p> 
 <ul> 
   <li> STK RIL: Low-level layer provided by the vendor plus <code>libril</code>.</li> 
index 2d3fcd6..0077447 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Device Requirements
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <p>While Android is designed to support a wide variety of hardware platforms and configurations, this section provides recommended minimum device requirements.</p>
@@ -56,4 +57,4 @@ pdk.version=1.0
   <li>QWERTY keyboard</li>
   <li>WiFi</li>
   <li>GPS</li>
-</ul>
\ No newline at end of file
+</ul>
index 96103a5..01f77bb 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Debugging with tcpdump and other tools
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 
@@ -86,4 +87,4 @@ vendor/google/tools/override-gservices url:calendar_sync_https_proxy \
 <h4>On the desktop:</h4>
 <ul>
   <li> <code>curl</code>: fetch URLs directly to emulate device requests</li>
-</ul>
\ No newline at end of file
+</ul>
index 0fe5221..2004e63 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Radio Layer Interface
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
index 5341954..1e9f527 100755 (executable)
@@ -1,5 +1,6 @@
 page.title=Wi-Fi
 pdk.version=1.0
+doc.type=guide
 @jd:body
 
 <div id="qv-wrapper">
diff --git a/pdk/docs/index.jd b/pdk/docs/index.jd
new file mode 100644 (file)
index 0000000..4175db7
--- /dev/null
@@ -0,0 +1,133 @@
+home=true
+@jd:body
+
+
+       <div id="mainBodyFixed" align="top">
+              <div id="mainBodyLeft">                  
+                   <h2>Android Open Source Project</h2>
+                   <!-- total max width is 520px -->
+                   <p> Android is the first free, open source, and fully customizable mobile platform. 
+                       Android offers a full stack: an operating system, middleware and key mobile applications.
+                       It also contains a rich set of APIs that allows third-party developers to develop great
+                       applications. </p>
+              </div><!-- end mainBodyLeft -->
+
+              <div id="mainBodyRight">
+                      <table id="rightColumn">
+                              <tr>
+                                      <td class="imageCell"><a href="{@docRoot}sdk/index.html"><img src="{@docRoot}assets/images/icon_download.jpg" style="padding:0" /></a></td>
+                                      <td>
+                                              <h2 class="green">Download</h2>
+                                              <p>The Android SDK has the tools, sample code, and docs you need to create great apps.  </p>
+                                              <p><a href="http://developer.android.com">Learn more &raquo;</a></p>
+                                      </td>
+                              </tr>
+                              <tr>
+                                      <td colspan="2"><div class="seperator">&nbsp;</div></td>
+                              </tr>
+                              <tr>
+                                      <td class="imageCell"><a href="http://www.android.com/market.html"><img src="{@docRoot}assets/images/icon_market.jpg" style="padding:0" /></a></td>
+                                      <td>
+                                              <h2 class="green">Publish</h2>
+                                              <p>Android Market is an open service that lets you distribute your apps to handsets.</p>
+                                              <p><a href="http://www.android.com/market.html">Learn more &raquo;</a></p>
+                                      </td>
+                              </tr>
+                              <tr>
+                                      <td colspan="2"><div class="seperator">&nbsp;</div></td>
+                              </tr>
+                              <tr>
+                                      <td class="imageCell"><a href="http://source.android.com"><img src="{@docRoot}assets/images/icon_contribute.jpg" style="padding:0" /></a></td>
+                                      <td>
+                                              <h2 class="green">Contribute</h2>
+                                              <p>Android Open Source Project gives you access to the entire platform source.</p>
+                                              <p><a href="http://source.android.com">Learn more &raquo;</a></p>
+                                      </td>
+                              </tr>
+                              <tr>
+                                      <td colspan="2"><div class="seperator">&nbsp;</div></td>
+                              </tr>
+                              <tr>
+                                      <td class="imageCell"><a href="http://www.youtube.com/user/androiddevelopers"><img src="{@docRoot}assets/images/video-droid.png" style="padding:0" /></a></td>
+                                      <td>
+                                              <h2 class="green">Watch</h2>
+                                              <object width="150" height="140"><param name="movie" value="http://www.youtube.com/v/GARMe7Km_gk&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/GARMe7Km_gk&hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="150" height="140"></embed></object>
+                                              <p style="margin-top:1em"><a href="{@docRoot}videos/index.html">More Android videos &raquo;</a></p>
+                                      </td>
+                              </tr>
+
+                      </table>
+              </div>
+       </div>
+
+<!--[if lte IE 6]>
+  <style>
+    #arrow-left {
+      margin:0 0 0 5px;
+    }
+    #arrow-right {
+      margin-left:0;
+    }
+    .app-list-container {
+      margin: 37px 0 0 23px;
+    }
+    div#list-clip { 
+      width:468px;
+    }
+  </style>
+<![endif]-->
+
+<script type="text/javascript">
+
+// * -- carousel dictionary -- * //
+  /* layout:  imgLeft, imgRight, imgTop
+     icon:    image for carousel entry. cropped (height:70px, width:90px)
+     name:    string for carousel entry
+     img:     image for bulletin post. cropped (height: 170, width:230px)
+     title:   header for bulletin (optional, insert "" value to skip
+     desc:    the bulletin post. must include html tags. 
+  */
+
+  var droidList = {
+    'sdk': {
+      'layout':"imgLeft",
+      'icon':"sdk-small.png",
+      'name':"SDK 1.5 r3",
+      'img':"sdk-large.png",
+      'title':"Android 1.5 SDK",
+      'desc': "<p>Android 1.5 SDK is now available. It includes new APIs for Android 1.5, updated developer tools, multiple platform versions, and a Google APIs add-on.</p><p><a href='{@docRoot}sdk/1.5_r3/index.html'>Download Android 1.5 SDK &raquo;</a></p>"
+    },
+    
+    'io': {
+      'layout':"imgLeft",
+      'icon':"io-small.png",
+      'name':"Google I/O",
+      'img':"io-large.png",
+      'title':"Google I/O Developer Conference",
+      'desc': "<p>The Google I/O developer conference took place May 27-28 in San Francisco. If you missed the conference, you can experience the Android sessions by viewing YouTube videos.</p><p><a href='{@docRoot}videos/index.html'>See the sessions from Google I/O &raquo;</a></p>"
+    },
+
+    'mapskey': {
+      'layout':"imgLeft",
+      'icon':"maps-small.png",
+      'name':"Maps API Key",
+      'img':"maps-large.png",
+      'title':"Maps API Key",
+      'desc':"<p>If you're writing an Android application that uses Google Maps (with MapView), you must register your application to obtain a Maps API Key. Without the key, your maps application will not work on Android devices. Obtaining a key requires just a couple of steps.</p><p><a href='http://code.google.com/android/add-ons/google-apis/maps-overview.html'>Learn more &raquo;</a></p>"
+    },
+
+    'devphone': {
+      'layout':"imgLeft",
+      'icon':"devphone-small.png",
+      'name':"Dev Phone 1",
+      'img':"devphone-large.png",
+      'title':"Android Dev Phone 1",
+      'desc': "<p>Run and debug your Android applications directly on this device. Modify and rebuild the Android operating system, and flash it onto the phone. The Android Dev Phone 1 is carrier independent, and available for purchase by any developer registered with <a href='http://market.android.com/publish'>Android Market</a>.</p><p><a href='/guide/developing/device.html#dev-phone-1'>Learn more about the Android Dev Phone 1 &raquo;</a></p>"
+    }
+
+  }
+</script>
+<script type="text/javascript" src="{@docRoot}assets/carousel.js"></script>
+<script type="text/javascript">
+  initCarousel("sdk");
+</script>
diff --git a/pdk/docs/licenses/index.jd b/pdk/docs/licenses/index.jd
new file mode 100644 (file)
index 0000000..a4f51dc
--- /dev/null
@@ -0,0 +1,47 @@
+home=true
+doc.type=licenses
+@jd:body
+
+<div id="mainBodyFixed">
+
+<p>
+   The Android Open Source Project uses a few open source initiative approved open source licenses to enable availability
+   of source code and to accept contributions from individuals and corporations.
+</p>
+
+<p>
+   <b>Android Open Source Project license</b>
+</p>
+
+<p>
+   The preferred license for the Android Open Source Project is Apache 2.0. Apache 2.0 is a commercial and open source
+   friendly open source license. The majority of the Android platform is licensed under the Apache 2.0 license. While
+   the project will strive to adhere to the preferred license, there may be exceptions which will be handled on a case-by-case
+   basis. For example, the Linux kernel patches are under the GPLv2 license with system exceptions, which can be found on kernel.org
+</p>
+
+<p>
+   <b>Contributor License Grants</b>
+</p>
+
+<p>
+   All individual contributors of ideas, code, or documentation to the Android Open Source Project will be required to
+   complete, sign, and submit an Individual Contributor License Grant. The grant can be executed online through the code
+   review tool. The agreement clearly defines the terms under which intellectual property has been contributed to the
+   Android Open Source Project.  This license is for your protection as a contributor as well as the protection of the
+   project; it does not change your rights to use your own contributions for any other purpose.
+</p>
+
+<p>
+   For a corporation that has assigned employees to work on the Android Open Source Project, a Corporate Contributor License
+   Grant is available. This version of the Grant allows a corporation to authorize contributions submitted by its designated
+   employees and to grant copyright and patent licenses. Note that a Corporate Contributor License Grant does not remove the
+   need for any developer to sign their own Individual Contributor License Grant as an individual, to cover any of their
+   contributions which are not owned by the corporation signing the Corporate Contributor License Grant.
+</p>
+
+<p>
+   Please note that we based our grants on the ones that the Apache Software Foundation uses, which can be found on its site.
+</p>
+
+</div>
diff --git a/pdk/docs/licenses/licenses_toc.cs b/pdk/docs/licenses/licenses_toc.cs
new file mode 100644 (file)
index 0000000..b2fa107
--- /dev/null
@@ -0,0 +1,21 @@
+<script type="text/javascript" language="JavaScript">
+<!--
+function nothing() {}
+-->
+</script>
+
+<ul>
+  <li> <h2> Referenced Licenses </h2>
+  <ul>
+    <li><a href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 license</a></li>
+    <li><a href="http://www.kernel.org/pub/linux/kernel/COPYING">GPL v2 license</a></li>
+  </ul>
+  </li>
+
+</ul>
+
+<script type="text/javascript">
+<!--
+    buildToggleLists();
+//-->
+</script>
diff --git a/pdk/docs/releases/index.jd b/pdk/docs/releases/index.jd
new file mode 100644 (file)
index 0000000..b3b7154
--- /dev/null
@@ -0,0 +1,7 @@
+home=true
+doc.type=releases
+@jd:body
+
+<p>
+  Some release information here
+</p>
diff --git a/pdk/docs/releases/releases_toc.cs b/pdk/docs/releases/releases_toc.cs
new file mode 100644 (file)
index 0000000..a1842b5
--- /dev/null
@@ -0,0 +1,22 @@
+<script type="text/javascript" language="JavaScript">
+<!--
+function nothing() {}
+-->
+</script>
+
+<ul>
+  <li> <h2> Android Releases </h2>
+  <ul>
+    <li><a href="xxx">Android 1.0</a></li>
+    <li><a href="xxx">Android 1.1</a></li>
+    <li><a href="xxx">Android 1.5</a></li>
+  </ul>
+  </li>
+
+</ul>
+
+<script type="text/javascript">
+<!--
+    buildToggleLists();
+//-->
+</script>
index fb34afa..032f5c2 100644 (file)
             </intent-filter>
         </activity-alias>
 
+        <activity android:name=".graphics.DensityActivity" android:label="Graphics/Density">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <!-- ************************************* -->
         <!--             MEDIA SAMPLES             -->
         <!-- ************************************* -->
index b0d1c7b..6b30303 100755 (executable)
@@ -1,19 +1,42 @@
-<p>The API Demos include sample code for many aspects of the Android APIs, from screen layout to Intent resolution.
+<p>The API Demos application includes a variety of small applications 
+that illustrate the use of various Android APIs. It includes samples of:
 </p>
+<ul>
+  <li>Notifications</li>  
+  <li>Alarms</li>  
+  <li>Progress Dialogs</li>  
+  <li>Intents</li>  
+  <li>Menus</li>  
+  <li>Search</li>  
+  <li>Persistent application state</li>  
+  <li>Preferences</li>  
+  <li>Background Services</li>  
+  <li>App Widgets</li>  
+  <li>Voice Recognition</li>  
+  <li>And many many more...</li>  
+</ul>
 
-<dl>
-    <dt><a href="src/com/example/android/apis/app/index.html">App</a></dt>
-    <dd></dd>
+<p>You'll notice that all the samples are included in a single Android project,
+so the application code and other resource files for all samples are batched together. 
+To help you find the code that's relevant to you, here's a directory that
+points to the program code for the different topics included in the project:</p>
 
-    <dt><a href="src/com/example/android/apis/content/index.html">Content</a></dt>
-    <dd></dd>
+<ul>
+    <li><a href="src/com/example/android/apis/animation/index.html">Animation</a></li>
+
+    <li><a href="src/com/example/android/apis/app/index.html">App</a></li>
+
+    <li><a href="src/com/example/android/apis/appwidget/index.html">App Widgets</a></li>
+
+    <li><a href="src/com/example/android/apis/content/index.html">Content</a></li>
     
-    <dt><a href="src/com/example/android/apis/view/index.html">View</a></dt>
-    <dd></dd>
+    <li><a href="src/com/example/android/apis/graphics/index.html">Graphics</a></li>
     
-    <dt><a href="src/com/example/android/apis/graphics/index.html">Graphics</a></dt>
-    <dd></dd>
+    <li><a href="src/com/example/android/apis/media/index.html">Media</a></li>
+    
+    <li><a href="src/com/example/android/apis/os/index.html">OS</a></li>
 
-    <dt><a href="src/com/example/android/apis/text/index.html">Text</a></dt>
-    <dd></dd>
-</dl>
+    <li><a href="src/com/example/android/apis/text/index.html">Text</a></li>
+    
+    <li><a href="src/com/example/android/apis/view/index.html">Views</a></li>
+</ul>
index 4513a1e..9d79b12 100644 (file)
@@ -8,4 +8,4 @@
 # project structure.
 
 # Project target.
-target=android-3
+target=android-4
diff --git a/samples/ApiDemos/res/drawable-hdpi/logo240dpi.png b/samples/ApiDemos/res/drawable-hdpi/logo240dpi.png
new file mode 100644 (file)
index 0000000..4d717a8
Binary files /dev/null and b/samples/ApiDemos/res/drawable-hdpi/logo240dpi.png differ
diff --git a/samples/ApiDemos/res/drawable-hdpi/npatch240dpi.9.png b/samples/ApiDemos/res/drawable-hdpi/npatch240dpi.9.png
new file mode 100644 (file)
index 0000000..a362b0f
Binary files /dev/null and b/samples/ApiDemos/res/drawable-hdpi/npatch240dpi.9.png differ
diff --git a/samples/ApiDemos/res/drawable-hdpi/reslogo240dpi.png b/samples/ApiDemos/res/drawable-hdpi/reslogo240dpi.png
new file mode 100644 (file)
index 0000000..4d717a8
Binary files /dev/null and b/samples/ApiDemos/res/drawable-hdpi/reslogo240dpi.png differ
diff --git a/samples/ApiDemos/res/drawable-hdpi/smlnpatch240dpi.9.png b/samples/ApiDemos/res/drawable-hdpi/smlnpatch240dpi.9.png
new file mode 100644 (file)
index 0000000..84bdcb0
Binary files /dev/null and b/samples/ApiDemos/res/drawable-hdpi/smlnpatch240dpi.9.png differ
diff --git a/samples/ApiDemos/res/drawable-hdpi/stylogo240dpi.png b/samples/ApiDemos/res/drawable-hdpi/stylogo240dpi.png
new file mode 100644 (file)
index 0000000..4d717a8
Binary files /dev/null and b/samples/ApiDemos/res/drawable-hdpi/stylogo240dpi.png differ
diff --git a/samples/ApiDemos/res/drawable-ldpi/logo120dpi.png b/samples/ApiDemos/res/drawable-ldpi/logo120dpi.png
new file mode 100644 (file)
index 0000000..46bbd5b
Binary files /dev/null and b/samples/ApiDemos/res/drawable-ldpi/logo120dpi.png differ
diff --git a/samples/ApiDemos/res/drawable-ldpi/npatch120dpi.9.png b/samples/ApiDemos/res/drawable-ldpi/npatch120dpi.9.png
new file mode 100644 (file)
index 0000000..0d8115b
Binary files /dev/null and b/samples/ApiDemos/res/drawable-ldpi/npatch120dpi.9.png differ
diff --git a/samples/ApiDemos/res/drawable-ldpi/reslogo120dpi.png b/samples/ApiDemos/res/drawable-ldpi/reslogo120dpi.png
new file mode 100644 (file)
index 0000000..46bbd5b
Binary files /dev/null and b/samples/ApiDemos/res/drawable-ldpi/reslogo120dpi.png differ
diff --git a/samples/ApiDemos/res/drawable-ldpi/smlnpatch120dpi.9.png b/samples/ApiDemos/res/drawable-ldpi/smlnpatch120dpi.9.png
new file mode 100644 (file)
index 0000000..de8d607
Binary files /dev/null and b/samples/ApiDemos/res/drawable-ldpi/smlnpatch120dpi.9.png differ
diff --git a/samples/ApiDemos/res/drawable-ldpi/stylogo120dpi.png b/samples/ApiDemos/res/drawable-ldpi/stylogo120dpi.png
new file mode 100644 (file)
index 0000000..46bbd5b
Binary files /dev/null and b/samples/ApiDemos/res/drawable-ldpi/stylogo120dpi.png differ
diff --git a/samples/ApiDemos/res/drawable-nodpi/logonodpi120.png b/samples/ApiDemos/res/drawable-nodpi/logonodpi120.png
new file mode 100644 (file)
index 0000000..46bbd5b
Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/logonodpi120.png differ
diff --git a/samples/ApiDemos/res/drawable-nodpi/logonodpi160.png b/samples/ApiDemos/res/drawable-nodpi/logonodpi160.png
new file mode 100644 (file)
index 0000000..c23b2ce
Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/logonodpi160.png differ
diff --git a/samples/ApiDemos/res/drawable-nodpi/logonodpi240.png b/samples/ApiDemos/res/drawable-nodpi/logonodpi240.png
new file mode 100644 (file)
index 0000000..4d717a8
Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/logonodpi240.png differ
diff --git a/samples/ApiDemos/res/drawable/logo160dpi.png b/samples/ApiDemos/res/drawable/logo160dpi.png
new file mode 100644 (file)
index 0000000..c23b2ce
Binary files /dev/null and b/samples/ApiDemos/res/drawable/logo160dpi.png differ
diff --git a/samples/ApiDemos/res/drawable/npatch160dpi.9.png b/samples/ApiDemos/res/drawable/npatch160dpi.9.png
new file mode 100644 (file)
index 0000000..44d89a9
Binary files /dev/null and b/samples/ApiDemos/res/drawable/npatch160dpi.9.png differ
diff --git a/samples/ApiDemos/res/drawable/reslogo160dpi.png b/samples/ApiDemos/res/drawable/reslogo160dpi.png
new file mode 100644 (file)
index 0000000..c23b2ce
Binary files /dev/null and b/samples/ApiDemos/res/drawable/reslogo160dpi.png differ
diff --git a/samples/ApiDemos/res/drawable/smlnpatch160dpi.9.png b/samples/ApiDemos/res/drawable/smlnpatch160dpi.9.png
new file mode 100644 (file)
index 0000000..76c4ae8
Binary files /dev/null and b/samples/ApiDemos/res/drawable/smlnpatch160dpi.9.png differ
diff --git a/samples/ApiDemos/res/drawable/stylogo160dpi.png b/samples/ApiDemos/res/drawable/stylogo160dpi.png
new file mode 100644 (file)
index 0000000..c23b2ce
Binary files /dev/null and b/samples/ApiDemos/res/drawable/stylogo160dpi.png differ
diff --git a/samples/ApiDemos/res/layout/density_image_views.xml b/samples/ApiDemos/res/layout/density_image_views.xml
new file mode 100644 (file)
index 0000000..6a91497
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/reslogo120dpi" />
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/reslogo160dpi" />
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/reslogo240dpi" />
+
+</LinearLayout>
diff --git a/samples/ApiDemos/res/layout/density_styled_image_views.xml b/samples/ApiDemos/res/layout/density_styled_image_views.xml
new file mode 100644 (file)
index 0000000..86c63bf
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <ImageView style="@style/ImageView120dpi" />
+    <ImageView style="@style/ImageView160dpi" />
+    <ImageView style="@style/ImageView240dpi" />
+
+</LinearLayout>
diff --git a/samples/ApiDemos/res/values-large-long/strings.xml b/samples/ApiDemos/res/values-large-long/strings.xml
new file mode 100644 (file)
index 0000000..7c392d8
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Large Long</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-large-notlong/strings.xml b/samples/ApiDemos/res/values-large-notlong/strings.xml
new file mode 100644 (file)
index 0000000..40328ea
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Large NotLong</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-large/strings.xml b/samples/ApiDemos/res/values-large/strings.xml
new file mode 100644 (file)
index 0000000..c9fb189
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Large</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-long/strings.xml b/samples/ApiDemos/res/values-long/strings.xml
new file mode 100644 (file)
index 0000000..5d15048
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Long</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-normal-long/strings.xml b/samples/ApiDemos/res/values-normal-long/strings.xml
new file mode 100644 (file)
index 0000000..01a0487
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Normal Long</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-normal-notlong/strings.xml b/samples/ApiDemos/res/values-normal-notlong/strings.xml
new file mode 100644 (file)
index 0000000..bea3f8d
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Normal NotLong</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-normal/strings.xml b/samples/ApiDemos/res/values-normal/strings.xml
new file mode 100644 (file)
index 0000000..16ff46f
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Normal</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-notlong/strings.xml b/samples/ApiDemos/res/values-notlong/strings.xml
new file mode 100644 (file)
index 0000000..a3e78db
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: NotLong</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-small-long/strings.xml b/samples/ApiDemos/res/values-small-long/strings.xml
new file mode 100644 (file)
index 0000000..8526f61
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Small Long</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-small-notlong/strings.xml b/samples/ApiDemos/res/values-small-notlong/strings.xml
new file mode 100644 (file)
index 0000000..bc8a676
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Small NotLong</string>
+</resources>
diff --git a/samples/ApiDemos/res/values-small/strings.xml b/samples/ApiDemos/res/values-small/strings.xml
new file mode 100644 (file)
index 0000000..56db730
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="density_title">Density: Small</string>
+</resources>
index c6ef8f6..a770d63 100644 (file)
 
     <string name="hide_me">Hide Me!</string>
 
+    <string name="density_title">Density: Unknown Screen</string>
+    
     <!-- ============================ -->
     <!--  media examples strings  -->
     <!-- ============================ -->
index 40e934e..3c9c681 100644 (file)
         <item name="android:textStyle">normal</item>
     </style>
 
+    <style name="ImageView120dpi">
+        <item name="android:src">@drawable/stylogo120dpi</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+
+    <style name="ImageView160dpi">
+        <item name="android:src">@drawable/stylogo160dpi</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+
+    <style name="ImageView240dpi">
+        <item name="android:src">@drawable/stylogo240dpi</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
 </resources>
index 92460e5..5ed1714 100644 (file)
@@ -16,8 +16,6 @@
 
 package com.example.android.apis;
 
-import com.example.android.apis.app.DefaultValues;
-
 import android.app.Application;
 import android.preference.PreferenceManager;
 
@@ -33,6 +31,7 @@ import android.preference.PreferenceManager;
  */
 public class ApiDemosApplication extends Application {
 
+    @Override
     public void onCreate() {
         /*
          * This populates the default values from the preferences XML file. See
@@ -41,6 +40,7 @@ public class ApiDemosApplication extends Application {
         PreferenceManager.setDefaultValues(this, R.xml.default_values, false);
     }
 
+    @Override
     public void onTerminate() {
     }
 }
index c3bead0..13ea3da 100644 (file)
@@ -22,14 +22,13 @@ import android.app.Activity;
 import android.app.NotificationManager;
 import android.os.Bundle;
 
-import java.util.Map;
-
 /**
  * This activity is run as the click activity for {@link IncomingMessage}.
  * When it comes up, it also clears the notification, because the "message"
  * has been "read."
  */
 public class IncomingMessageView extends Activity {
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.incoming_message_view);
index 6977d3e..61aba63 100644 (file)
@@ -20,14 +20,11 @@ import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProvider;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.SystemClock;
 import android.util.Log;
 import android.widget.RemoteViews;
 
-import java.util.ArrayList;
-
 // Need the following import to get access to the app resources, since this
 // class is in a sub-package.
 import com.example.android.apis.R;
@@ -51,6 +48,7 @@ public class ExampleAppWidgetProvider extends AppWidgetProvider {
     // log tag
     private static final String TAG = "ExampleAppWidgetProvider";
 
+    @Override
     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
         Log.d(TAG, "onUpdate");
         // For each widget that needs an update, get the text that we should display:
@@ -65,6 +63,7 @@ public class ExampleAppWidgetProvider extends AppWidgetProvider {
         }
     }
     
+    @Override
     public void onDeleted(Context context, int[] appWidgetIds) {
         Log.d(TAG, "onDeleted");
         // When the user deletes the widget, delete the preference associated with it.
@@ -74,6 +73,7 @@ public class ExampleAppWidgetProvider extends AppWidgetProvider {
         }
     }
 
+    @Override
     public void onEnabled(Context context) {
         Log.d(TAG, "onEnabled");
         // When the first widget is created, register for the TIMEZONE_CHANGED and TIME_CHANGED
@@ -87,11 +87,11 @@ public class ExampleAppWidgetProvider extends AppWidgetProvider {
                 PackageManager.DONT_KILL_APP);
     }
 
+    @Override
     public void onDisabled(Context context) {
         // When the first widget is created, stop listening for the TIMEZONE_CHANGED and
         // TIME_CHANGED broadcasts.
         Log.d(TAG, "onDisabled");
-        Class clazz = ExampleBroadcastReceiver.class;
         PackageManager pm = context.getPackageManager();
         pm.setComponentEnabledSetting(
                 new ComponentName("com.example.android.apis", ".appwidget.ExampleBroadcastReceiver"),
index f6e01ad..cf23f3d 100644 (file)
 package com.example.android.apis.appwidget;
 
 import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProvider;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.os.SystemClock;
 import android.util.Log;
-import android.widget.RemoteViews;
 
 import java.util.ArrayList;
 
-// Need the following import to get access to the app resources, since this
-// class is in a sub-package.
-import com.example.android.apis.R;
-
 /**
  * A BroadcastReceiver that listens for updates for the ExampleAppWidgetProvider.  This
  * BroadcastReceiver starts off disabled, and we only enable it when there is a widget
@@ -39,6 +31,7 @@ import com.example.android.apis.R;
  */
 public class ExampleBroadcastReceiver extends BroadcastReceiver {
 
+    @Override
     public void onReceive(Context context, Intent intent) {
         Log.d("ExmampleBroadcastReceiver", "intent=" + intent);
 
@@ -48,8 +41,8 @@ public class ExampleBroadcastReceiver extends BroadcastReceiver {
         if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)
                 || action.equals(Intent.ACTION_TIME_CHANGED)) {
             AppWidgetManager gm = AppWidgetManager.getInstance(context);
-            ArrayList<Integer> appWidgetIds = new ArrayList();
-            ArrayList<String> texts = new ArrayList();
+            ArrayList<Integer> appWidgetIds = new ArrayList<Integer>();
+            ArrayList<String> texts = new ArrayList<String>();
 
             ExampleAppWidgetConfigure.loadAllTitlePrefs(context, appWidgetIds, texts);
 
index ceff150..e3cf976 100644 (file)
@@ -78,6 +78,7 @@ class Preview extends SurfaceView implements SurfaceHolder.Callback {
         // Because the CameraDevice object is not a shared resource, it's very
         // important to release it when the activity is paused.
         mCamera.stopPreview();
+        mCamera.release();
         mCamera = null;
     }
 
index cc4a0d4..7588180 100644 (file)
@@ -16,7 +16,6 @@
 
 package com.example.android.apis.graphics;
 
-import android.R;
 import android.os.Bundle;
 import android.app.Dialog;
 import android.content.Context;
@@ -218,7 +217,7 @@ public class ColorPickerDialog extends Dialog {
         mInitialColor = initialColor;
     }
 
-
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         OnColorChangedListener l = new OnColorChangedListener() {
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/DensityActivity.java b/samples/ApiDemos/src/com/example/android/apis/graphics/DensityActivity.java
new file mode 100644 (file)
index 0000000..10c42a7
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.graphics;
+
+//Need the following import to get access to the app resources, since this
+//class is in a sub-package.
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.ScrollView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+/**
+ * This activity demonstrates various ways density can cause the scaling of
+ * bitmaps and drawables.
+ */
+public class DensityActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final LayoutInflater li = (LayoutInflater)getSystemService(
+                LAYOUT_INFLATER_SERVICE);
+        
+        this.setTitle(R.string.density_title);
+        LinearLayout root = new LinearLayout(this);
+        root.setOrientation(LinearLayout.VERTICAL);
+
+        LinearLayout layout = new LinearLayout(this);
+        addBitmapDrawable(layout, R.drawable.logo120dpi, true);
+        addBitmapDrawable(layout, R.drawable.logo160dpi, true);
+        addBitmapDrawable(layout, R.drawable.logo240dpi, true);
+        addLabelToRoot(root, "Prescaled bitmap in drawable");
+        addChildToRoot(root, layout);
+
+        layout = new LinearLayout(this);
+        addBitmapDrawable(layout, R.drawable.logo120dpi, false);
+        addBitmapDrawable(layout, R.drawable.logo160dpi, false);
+        addBitmapDrawable(layout, R.drawable.logo240dpi, false);
+        addLabelToRoot(root, "Autoscaled bitmap in drawable");
+        addChildToRoot(root, layout);
+
+        layout = new LinearLayout(this);
+        addResourceDrawable(layout, R.drawable.logo120dpi);
+        addResourceDrawable(layout, R.drawable.logo160dpi);
+        addResourceDrawable(layout, R.drawable.logo240dpi);
+        addLabelToRoot(root, "Prescaled resource drawable");
+        addChildToRoot(root, layout);
+
+        layout = (LinearLayout)li.inflate(R.layout.density_image_views, null);
+        addLabelToRoot(root, "Inflated layout");
+        addChildToRoot(root, layout);
+        
+        layout = (LinearLayout)li.inflate(R.layout.density_styled_image_views, null);
+        addLabelToRoot(root, "Inflated styled layout");
+        addChildToRoot(root, layout);
+        
+        layout = new LinearLayout(this);
+        addCanvasBitmap(layout, R.drawable.logo120dpi, true);
+        addCanvasBitmap(layout, R.drawable.logo160dpi, true);
+        addCanvasBitmap(layout, R.drawable.logo240dpi, true);
+        addLabelToRoot(root, "Prescaled bitmap");
+        addChildToRoot(root, layout);
+
+        layout = new LinearLayout(this);
+        addCanvasBitmap(layout, R.drawable.logo120dpi, false);
+        addCanvasBitmap(layout, R.drawable.logo160dpi, false);
+        addCanvasBitmap(layout, R.drawable.logo240dpi, false);
+        addLabelToRoot(root, "Autoscaled bitmap");
+        addChildToRoot(root, layout);
+
+        layout = new LinearLayout(this);
+        addResourceDrawable(layout, R.drawable.logonodpi120);
+        addResourceDrawable(layout, R.drawable.logonodpi160);
+        addResourceDrawable(layout, R.drawable.logonodpi240);
+        addLabelToRoot(root, "No-dpi resource drawable");
+        addChildToRoot(root, layout);
+
+        layout = new LinearLayout(this);
+        addNinePatchResourceDrawable(layout, R.drawable.smlnpatch120dpi);
+        addNinePatchResourceDrawable(layout, R.drawable.smlnpatch160dpi);
+        addNinePatchResourceDrawable(layout, R.drawable.smlnpatch240dpi);
+        addLabelToRoot(root, "Prescaled 9-patch resource drawable");
+        addChildToRoot(root, layout);
+
+        setContentView(scrollWrap(root));
+    }
+
+    private View scrollWrap(View view) {
+        ScrollView scroller = new ScrollView(this);
+        scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.FILL_PARENT,
+                ScrollView.LayoutParams.FILL_PARENT));
+        return scroller;
+    }
+
+    private void addLabelToRoot(LinearLayout root, String text) {
+        TextView label = new TextView(this);
+        label.setText(text);
+        root.addView(label, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT));
+    }
+
+    private void addChildToRoot(LinearLayout root, LinearLayout layout) {
+        root.addView(layout, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT));
+    }
+
+    private void addBitmapDrawable(LinearLayout layout, int resource, boolean scale) {
+        Bitmap bitmap;
+        bitmap = loadAndPrintDpi(resource, scale);
+
+        View view = new View(this);
+
+        final BitmapDrawable d = new BitmapDrawable(getResources(), bitmap);
+        if (!scale) d.setTargetDensity(getResources().getDisplayMetrics());
+        view.setBackgroundDrawable(d);
+
+        view.setLayoutParams(new LinearLayout.LayoutParams(d.getIntrinsicWidth(),
+                d.getIntrinsicHeight()));
+        layout.addView(view);
+    }
+
+    private void addResourceDrawable(LinearLayout layout, int resource) {
+        View view = new View(this);
+
+        final Drawable d = getResources().getDrawable(resource);
+        view.setBackgroundDrawable(d);
+
+        view.setLayoutParams(new LinearLayout.LayoutParams(d.getIntrinsicWidth(),
+                d.getIntrinsicHeight()));
+        layout.addView(view);
+    }
+
+    private void addCanvasBitmap(LinearLayout layout, int resource, boolean scale) {
+        Bitmap bitmap;
+        bitmap = loadAndPrintDpi(resource, scale);
+
+        ScaledBitmapView view = new ScaledBitmapView(this, bitmap);
+
+        view.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT));
+        layout.addView(view);
+    }
+
+    private void addNinePatchResourceDrawable(LinearLayout layout, int resource) {
+        View view = new View(this);
+
+        final Drawable d = getResources().getDrawable(resource);
+        view.setBackgroundDrawable(d);
+
+        Log.i("foo", "9-patch #" + Integer.toHexString(resource)
+                + " w=" + d.getIntrinsicWidth() + " h=" + d.getIntrinsicHeight());
+        view.setLayoutParams(new LinearLayout.LayoutParams(
+                d.getIntrinsicWidth()*2, d.getIntrinsicHeight()*2));
+        layout.addView(view);
+    }
+
+    private Bitmap loadAndPrintDpi(int id, boolean scale) {
+        Bitmap bitmap;
+        if (scale) {
+            bitmap = BitmapFactory.decodeResource(getResources(), id);
+        } else {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inScaled = false;
+            bitmap = BitmapFactory.decodeResource(getResources(), id, opts);
+        }
+        return bitmap;
+    }
+
+    private class ScaledBitmapView extends View {
+        private Bitmap mBitmap;
+
+        public ScaledBitmapView(Context context, Bitmap bitmap) {
+            super(context);
+            mBitmap = bitmap;
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            final DisplayMetrics metrics = getResources().getDisplayMetrics();
+            setMeasuredDimension(
+                    mBitmap.getScaledWidth(metrics),
+                    mBitmap.getScaledHeight(metrics));
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
+        }
+    }
+}
index 7d4c7c1..7478cf4 100644 (file)
@@ -18,31 +18,34 @@ package com.example.android.apis.graphics.kube;
 
 public class GLColor {
 
-       public final int red;
-       public final int green;
-       public final int blue;
-       public final int alpha;
-       
-       public GLColor(int red, int green, int blue, int alpha) {
-               this.red = red;
-               this.green = green;
-               this.blue = blue;
-               this.alpha = alpha;
-       }
+    public final int red;
+    public final int green;
+    public final int blue;
+    public final int alpha;
 
-       public GLColor(int red, int green, int blue) {
-               this.red = red;
-               this.green = green;
-               this.blue = blue;
-               this.alpha = 0x10000;
-       }
-       
-       public boolean equals(Object other) {
-               if (other instanceof GLColor) {
-                       GLColor color = (GLColor)other;
-                       return (red == color.red && green == color.green &&
-                                       blue == color.blue && alpha == color.alpha);
-               }
-               return false;
-       }
+    public GLColor(int red, int green, int blue, int alpha) {
+        this.red = red;
+        this.green = green;
+        this.blue = blue;
+        this.alpha = alpha;
+    }
+
+    public GLColor(int red, int green, int blue) {
+        this.red = red;
+        this.green = green;
+        this.blue = blue;
+        this.alpha = 0x10000;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof GLColor) {
+            GLColor color = (GLColor)other;
+            return (red == color.red &&
+                    green == color.green &&
+                    blue == color.blue &&
+                    alpha == color.alpha);
+        }
+        return false;
+    }
 }
index b5cd873..6e3bf68 100644 (file)
@@ -19,70 +19,71 @@ package com.example.android.apis.graphics.kube;
 import java.nio.IntBuffer;
 
 public class GLVertex {
-       
-       public float x;
-       public float y;
-       public float z;
-       final short index;      // index in vertex table
-       GLColor color;
-       
-       GLVertex() {
-               this.x = 0;
-               this.y = 0;
-               this.z = 0;
-               this.index = -1;
-       }
 
-       GLVertex(float x, float y, float z, int index) {
-               this.x = x;
-               this.y = y;
-               this.z = z;
-               this.index = (short)index;
-       }
+    public float x;
+    public float y;
+    public float z;
+    final short index; // index in vertex table
+    GLColor color;
 
-       public boolean equals(Object other) {
-               if (other instanceof GLVertex) {
-                       GLVertex v = (GLVertex)other;
-                       return (x == v.x && y == v.y && z == v.z);
-               }
-               return false;
-       }
+    GLVertex() {
+        this.x = 0;
+        this.y = 0;
+        this.z = 0;
+        this.index = -1;
+    }
+
+    GLVertex(float x, float y, float z, int index) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.index = (short)index;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof GLVertex) {
+            GLVertex v = (GLVertex)other;
+            return (x == v.x && y == v.y && z == v.z);
+        }
+        return false;
+    }
 
     static public int toFixed(float x) {
-       return (int)(x*65536.0f);
+        return (int)(x * 65536.0f);
     }
 
-       public void put(IntBuffer vertexBuffer, IntBuffer colorBuffer) {
-               vertexBuffer.put(toFixed(x));
-               vertexBuffer.put(toFixed(y));
-               vertexBuffer.put(toFixed(z));
-               if (color == null) {
-                       colorBuffer.put(0);
-                       colorBuffer.put(0);
-                       colorBuffer.put(0);
-                       colorBuffer.put(0);
-               } else {
-                       colorBuffer.put(color.red);
-                       colorBuffer.put(color.green);
-                       colorBuffer.put(color.blue);
-                       colorBuffer.put(color.alpha);
-               }
-       }
-       
-       public void update(IntBuffer vertexBuffer, M4 transform) {
-               // skip to location of vertex in mVertex buffer
-               vertexBuffer.position(index * 3);
-       
-               if (transform == null) {
-                       vertexBuffer.put(toFixed(x));
-                       vertexBuffer.put(toFixed(y));
-                       vertexBuffer.put(toFixed(z));                   
-               } else {
-                       GLVertex temp = new GLVertex();
-                       transform.multiply(this, temp);
-                       vertexBuffer.put(toFixed(temp.x));
-                       vertexBuffer.put(toFixed(temp.y));
-                       vertexBuffer.put(toFixed(temp.z));
-               }
-       }
+    public void put(IntBuffer vertexBuffer, IntBuffer colorBuffer) {
+        vertexBuffer.put(toFixed(x));
+        vertexBuffer.put(toFixed(y));
+        vertexBuffer.put(toFixed(z));
+        if (color == null) {
+            colorBuffer.put(0);
+            colorBuffer.put(0);
+            colorBuffer.put(0);
+            colorBuffer.put(0);
+        } else {
+            colorBuffer.put(color.red);
+            colorBuffer.put(color.green);
+            colorBuffer.put(color.blue);
+            colorBuffer.put(color.alpha);
+        }
+    }
+
+    public void update(IntBuffer vertexBuffer, M4 transform) {
+        // skip to location of vertex in mVertex buffer
+        vertexBuffer.position(index * 3);
+
+        if (transform == null) {
+            vertexBuffer.put(toFixed(x));
+            vertexBuffer.put(toFixed(y));
+            vertexBuffer.put(toFixed(z));
+        } else {
+            GLVertex temp = new GLVertex();
+            transform.multiply(this, temp);
+            vertexBuffer.put(toFixed(temp.x));
+            vertexBuffer.put(toFixed(temp.y));
+            vertexBuffer.put(toFixed(temp.z));
+        }
+    }
 }
index 9977041..58b03da 100644 (file)
@@ -18,7 +18,6 @@ package com.example.android.apis.graphics.kube;
 
 import android.opengl.GLSurfaceView;
 
-import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
 
index 4b89167..1303d35 100644 (file)
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.example.android.apis.media;
 
 import com.example.android.apis.R;
index 39930f6..b209ce9 100644 (file)
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.example.android.apis.media;
 
 import android.app.Activity;
@@ -23,6 +39,7 @@ public class MediaPlayerDemo_Audio extends Activity {
 
     private TextView tx;
 
+    @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         tx = new TextView(this);
index 601c5a3..9853d67 100644 (file)
@@ -1,10 +1,24 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.example.android.apis.media;
 
 import com.example.android.apis.R;
-import com.example.android.apis.app.AlarmController;
 
 import android.app.Activity;
-import android.graphics.PixelFormat;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
 import android.media.MediaPlayer.OnBufferingUpdateListener;
@@ -15,9 +29,6 @@ import android.os.Bundle;
 import android.util.Log;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
 import android.widget.Toast;
 
 
@@ -46,6 +57,7 @@ public class MediaPlayerDemo_Video extends Activity implements
      * 
      * Called when the activity is first created.
      */
+    @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         setContentView(R.layout.mediaplayer_2);
index a4fcf5f..e72c077 100644 (file)
@@ -1,11 +1,24 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.example.android.apis.media;
 
 import com.example.android.apis.R;
 import android.app.Activity;
-import android.graphics.PixelFormat;
-import android.net.Uri;
 import android.os.Bundle;
-import android.util.Log;
 import android.widget.MediaController;
 import android.widget.Toast;
 import android.widget.VideoView;
index d60e702..ab7898c 100644 (file)
@@ -20,8 +20,6 @@ import com.example.android.apis.R;
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 
 /**
  * Baseline alignment includes elements within nested vertical
@@ -29,7 +27,7 @@ import android.widget.TextView;
  */
 public class BaselineNested1 extends Activity {
 
-
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
index 32574d7..1b2c585 100644 (file)
@@ -20,8 +20,6 @@ import com.example.android.apis.R;
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 
 /**
  * Baseline alignment includes an element within a nested horizontal
@@ -29,7 +27,7 @@ import android.widget.TextView;
  */
 public class BaselineNested2 extends Activity {
 
-
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
index 3377041..854a10f 100644 (file)
@@ -20,8 +20,6 @@ import com.example.android.apis.R;
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 
 /**
  * Baseline alignment includes a {@link android.widget.LinearLayout}
@@ -29,7 +27,7 @@ import android.widget.TextView;
  */
 public class BaselineNested3 extends Activity {
 
-
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
index 77bf8e0..5ffd847 100644 (file)
@@ -23,6 +23,7 @@ import android.os.Bundle;
 
 public class Focus2 extends Activity {
 
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
index fc3f6df..c952a64 100644 (file)
@@ -25,6 +25,7 @@ public class Focus3 extends Activity {
     private Button mTopButton;
     private Button mBottomButton;
 
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
index 3ef8403..fe415bd 100644 (file)
@@ -199,6 +199,7 @@ public class InternalSelectionView extends View {
     /* (non-Javadoc)
     * @see android.view.KeyEvent.Callback#onKeyDown(int, android.view.KeyEvent)
     */
+    @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch(event.getKeyCode()) {
             case KeyEvent.KEYCODE_DPAD_UP:
index 1c00145..9a240f9 100644 (file)
@@ -17,7 +17,6 @@
 package com.example.android.apis.view;
 
 import android.app.ListActivity;
-import android.content.Context;
 import android.os.Bundle;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
index edc2926..5986936 100644 (file)
@@ -21,8 +21,6 @@ import com.example.android.apis.R;
 import android.app.ListActivity;
 import android.os.Bundle;
 import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.view.View;
 
 public class LayoutAnimation3 extends ListActivity {
     @Override
index 8ed8a36..ced28a7 100644 (file)
@@ -25,7 +25,6 @@ import android.os.Bundle;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
-import android.widget.Gallery;
 import android.widget.GridView;
 import android.widget.ImageView;
 
index 6eeb429..bfda4a5 100644 (file)
@@ -19,20 +19,7 @@ package com.example.android.apis.view;
 import com.example.android.apis.R;
 
 import android.app.Activity;
-import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.GridLayoutAnimationController;
-import android.widget.BaseAdapter;
-import android.widget.Gallery;
-import android.widget.GridView;
-import android.widget.ImageView;
-
-import java.util.List;
 
 public class LayoutAnimation7 extends Activity {
     @Override
index 72a8b82..76b4469 100644 (file)
@@ -49,10 +49,12 @@ public class List5 extends ListActivity {
             return mStrings.length;
         }
 
+        @Override
         public boolean areAllItemsEnabled() {
             return false;
         }
 
+        @Override
         public boolean isEnabled(int position) {
             return !mStrings[position].startsWith("-");
         }
diff --git a/samples/Home/_index.html b/samples/Home/_index.html
new file mode 100644 (file)
index 0000000..7581eed
--- /dev/null
@@ -0,0 +1,9 @@
+<p>This is a sample Home application. The Home application that users use
+to launch applications by default is an ordinary application itself.
+A user can install additional Home applications and pick which one to use as the
+default Home. This is a sample of such an alternative.</p>
+
+<p>This is actually the source of an old incarnation of the default Home application,
+which may look familiar if you played with some of the older pre-1.0 SDKs.</p>
+
+<img alt="" src="../images/HomeSample.png" />
\ No newline at end of file
diff --git a/samples/JetBoy/_index.html b/samples/JetBoy/_index.html
new file mode 100644 (file)
index 0000000..f7ce9ef
--- /dev/null
@@ -0,0 +1,24 @@
+<p>JetBoy is a sample game that demonstrates the use of the 
+<a href="../../../reference/android/media/JetPlayer.html">android.media.JetPlayer</a> 
+class to implement an interactive music soundtrack in an application. JetBoy uses 
+<a href="../../../guide/topics/media/index.html#jet">JET content created with
+JetCreator</a> and game-generated events fed to JetPlayer 
+to adapt the soundtrack to the user actions. Listen to how the melody picks 
+up when you start shooting asteroids, how you are congratulated when you 
+hit several asteroids in a row, or destroy enough of them in the allotted time.</p>
+
+<p>The JetBoyView.java file in JetBoy illustrates the loading of JET content 
+(loading a file, queuing segments), its playback, and how to alter what 
+is currently playing (use of clips and mute masks).</p>
+
+<p class="note"><strong>Note:</strong>
+The <code>JETBOY_content/</code> directory is empty in this online presentation of 
+the application. For complete access to all the JetBoy files, see the sample code included
+in the SDK, located at <code>&lt;sdk>/platforms/&lt;platform>/samples/JetBoy/</code>.
+</p>
+
+<p><strong>See also:</strong><br/>
+<a href="../../../guide/topics/media/jet/jetcreator_manual.html">SONiVOX JETCreator User Manual</a><br/>
+<a href="../../../reference/android/media/JetPlayer.html">JetPlayer class</a></p>
+
+<img alt="" src="../images/JetBoy.png"  />
index 02daeef..c2800eb 100644 (file)
@@ -1,12 +1,12 @@
 <p>A sample game.  Your objective: to land on the moon.
-It demonstrates...
+It demonstrates:
 <ul> 
-<li>loading and drawing resources
-<li>taking keystrokes
-<li>animating by calling invalidate() from draw()
-<li>handling onPause() in an animation
-<li>and many other goodies...
+<li>Loading and drawing resources</li>
+<li>Taking keystrokes</li>
+<li>Animating by calling invalidate() from draw()</li>
+<li>Handling onPause() in an animation</li>
+<li>And more...</li>
 </ul>
 </p>
 
-<img height="220px" width="320px" alt="Lunar Lander Example" class="gallery"  src="sample_lunarlander.png" >
\ No newline at end of file
+<img alt=""  src="../images/sample_lunarlander.png" >
\ No newline at end of file
diff --git a/samples/LunarLander/sample_lunarlander.png b/samples/LunarLander/sample_lunarlander.png
deleted file mode 100644 (file)
index ea1ef14..0000000
Binary files a/samples/LunarLander/sample_lunarlander.png and /dev/null differ
index 2a18969..093022b 100644 (file)
@@ -1,12 +1,19 @@
 <p>A simple note pad application.
-It demonstrates...
+It demonstrates:</p>
 <ul> 
-<li>using views
-<li>accessing a database
-<li>using an intent to open a new window
-<li>managing activity lifecycle
-<li>and many other goodies...
+<li>Using views</li>
+<li>Accessing a database</li>
+<li>Using an intent to open a new window</li>
+<li>Managing activity lifecycle</li>
+<li>And more...</li>
 </ul>
-</p>
-<img alt="Note Pad Example" class="gallery"  src="sample_notepad.png" >
-<img alt="Note Pad Example" class="gallery"  src="sample_note.png"  >
+
+<p class="note">Please notice that this is not the same
+notepad code that's used for the <a href="../../../guide/tutorials/notepad/index.html">Notepad Tutorial</a>. 
+They are similar in nature, but there are several differences in implementation &mdash; the tutorial
+is slightly more simple. If you're new to 
+Android development, we suggest you start with the tutorial, then visit this
+code later to see more sample code.</p>
+
+<img alt="" src="../images/sample_notepad.png" />
+<img alt="" src="../images/sample_note.png" />
diff --git a/samples/NotePad/sample_note.png b/samples/NotePad/sample_note.png
deleted file mode 100644 (file)
index cea1a08..0000000
Binary files a/samples/NotePad/sample_note.png and /dev/null differ
diff --git a/samples/NotePad/sample_notepad.png b/samples/NotePad/sample_notepad.png
deleted file mode 100644 (file)
index c498847..0000000
Binary files a/samples/NotePad/sample_notepad.png and /dev/null differ
diff --git a/samples/SearchableDictionary/Android.mk b/samples/SearchableDictionary/Android.mk
new file mode 100755 (executable)
index 0000000..8c5fdf4
--- /dev/null
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := SearchableDictionary
+
+include $(BUILD_PACKAGE)
diff --git a/samples/SearchableDictionary/AndroidManifest.xml b/samples/SearchableDictionary/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..93cd47b
--- /dev/null
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.example.android.searchabledict">
+
+    <uses-sdk android:minSdkVersion="4" />
+
+    <application android:label="@string/app_name"
+            android:icon="@drawable/ic_dictionary">
+
+        <!-- The default activity of the app.  Can also display search results. -->
+        <activity android:name=".SearchableDictionary"
+                android:label="@string/app_name"
+                android:theme="@android:style/Theme.NoTitleBar">
+
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+
+            <!-- Receives the search request. -->
+            <intent-filter>
+                <action android:name="android.intent.action.SEARCH" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+
+            <!-- Points to searchable meta data. -->
+            <meta-data android:name="android.app.searchable"
+                    android:resource="@xml/searchable"/>
+        </activity>
+
+        <!-- Displays the definition of a word. -->
+        <activity android:name=".WordActivity"
+                android:theme="@android:style/Theme.NoTitleBar"/>
+
+        <!-- Provides search suggestions for words and their definitions. -->
+        <provider android:name="DictionaryProvider"
+                android:authorities="dictionary"
+                android:syncable="false" />
+
+    </application>
+</manifest>
diff --git a/samples/SearchableDictionary/_index.html b/samples/SearchableDictionary/_index.html
new file mode 100644 (file)
index 0000000..de3345e
--- /dev/null
@@ -0,0 +1,21 @@
+<p>A sample application that demonstrates Android's search framework.</p>
+
+<p>This application includes a dictionary of words. By invoking a search inside the app
+(via the device search button or Menu > Search), a local search will be performed
+across the dictionary. As you type, suggestions will appear, which you can select
+to view the full definition. You can also execute the search to view all word definitions
+that match the entered text.</p>
+
+<p>The application also grants content provider privileges to 
+Quick Search Box, Android's system-wide search tool. This means that the
+dictionary definitions can be offered as search suggestions outside of the application,
+when text is entered into Quick Search Box.</p>
+       
+<p>See also:</p>
+<ul>
+  <li><a href="../../../reference/android/app/SearchManager.html">SearchManager</a></li>
+  <li><a href="../../../reference/android/content/ContentProvider.html">ContentProvider</a></li>
+</ul>
+
+<img src="../images/SearchableDictionary1.png" />
+<img src="../images/SearchableDictionary2.png" />
\ No newline at end of file
diff --git a/samples/SearchableDictionary/res/drawable/ic_dictionary.png b/samples/SearchableDictionary/res/drawable/ic_dictionary.png
new file mode 100644 (file)
index 0000000..64eb294
Binary files /dev/null and b/samples/SearchableDictionary/res/drawable/ic_dictionary.png differ
diff --git a/samples/SearchableDictionary/res/layout/main.xml b/samples/SearchableDictionary/res/layout/main.xml
new file mode 100644 (file)
index 0000000..d0dd522
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+    <TextView
+            android:id="@+id/textField"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/search_instructions"/>
+
+    <ListView
+            android:id="@+id/list"
+            android:layout_width="fill_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"/>
+</LinearLayout>
+
+
diff --git a/samples/SearchableDictionary/res/layout/word.xml b/samples/SearchableDictionary/res/layout/word.xml
new file mode 100644 (file)
index 0000000..ba1a3da
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+    <TextView
+            android:id="@+id/word"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textSize="30sp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/search_instructions"
+            android:layout_marginLeft="10dip"
+            android:layout_marginTop="5dip" />
+    <TextView
+            android:id="@+id/definition"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/search_instructions"
+            android:layout_marginLeft="10dip" />
+</LinearLayout>
diff --git a/samples/SearchableDictionary/res/raw/definitions.txt b/samples/SearchableDictionary/res/raw/definitions.txt
new file mode 100644 (file)
index 0000000..e4e1cbd
--- /dev/null
@@ -0,0 +1,997 @@
+abbey - n. a monastery ruled by an abbot
+abide - v. dwell; inhabit or live in
+abound - v. be abundant or plentiful; exist in large quantities
+absence - n. the state of being absent
+absorb - v. assimilate or take in
+abstinence - n. practice of refraining from indulging an appetite especially alcohol
+absurd - j. inconsistent with reason or logic or common sense
+abundant - j. present in great quantity
+abusive - j. characterized by physical or psychological maltreatment
+academic - j. associated with school or learning
+academy - n. a school for special training
+accept - v. consider or hold as true
+access - v. reach or gain access to
+accessible - j. easily obtained
+acclaim - n. enthusiastic approval
+accommodate - v. provide with something desired or needed
+accompany - v. go or travel along with
+accomplish - v. to gain with effort
+account - v. furnish a justifying analysis or explanation
+accurate - j. conforming exactly or almost exactly to fact or to a standard or performing with total accuracy
+accusation - n. an assertion that someone is guilty of a fault or offence
+accuse - v. blame for, make a claim of wrongdoing or misbehavior against
+acid - j. biting, sarcastic, or scornful
+acknowledge - v. declare to be true or admit the existence or reality or truth of
+acquire - v. come into the possession of something concrete or abstract
+acquisition - n. something acquired or received
+adamant - j. impervious to pleas, persuasion, requests, reason
+adjacent - j. having a common boundary or edge; abutting; touching
+administrator - n. someone who manages a government agency or department
+advent - n. arrival that has been awaited (especially of something momentous)
+adverse - j. contrary to your interests or welfare
+advisory - n. an announcement that usually advises or warns the public of some threat
+advocacy - n. active support of an idea or cause etc.; especially the act of pleading or arguing for something
+advocate - v. speak, plead, or argue in favor of
+affect - n. the conscious subjective aspect of feeling or emotion
+affirmative - j. affirming or giving assent
+aggression - n. violent action that is hostile and usually unprovoked
+airy - j. not practical or realizable; speculative
+album - n. a book of blank pages with pockets or envelopes; for organizing photographs or stamp collections etc
+alcohol - n. a liquor or brew containing alcohol as the active agent
+alien - j. being or from or characteristic of another place or part of the world
+allegiance - n. the loyalty that citizens owe to their country (or subjects to their sovereign)
+alley - n. a narrow street with walls on both sides
+alliance - n. a formal agreement establishing an association or alliance between nations or other groups to achieve a particular aim
+ally - n. an associate who provides cooperation or assistance
+altar - n. a raised structure on which gifts or sacrifices to a god are made
+alter - v. cause to change; make different; cause a transformation
+alternate - j. serving or used in place of another
+alternative - j. serving or used in place of another
+altitude - n. elevation especially above sea level or above the earth's surface
+amateur - j. lacking professional skill or expertise
+ambiguous - j. having more than one possible meaning
+ambitious - j. having a strong desire for success or achievement
+ambivalent - j. uncertain or unable to decide about what course to follow
+analogy - n. drawing a comparison in order to show a similarity in some respect
+analyst - n. someone who is skilled at analyzing data
+analyze - v. break down into components or essential features
+anchor - v. fix firmly and stably
+annual - j. occurring or payable every year
+anonymous - j. having no known name or identity or known source
+antichrist - n. the adversary of Christ (or Christianity) mentioned in the New Testament
+antique - j. made in or typical of earlier times and valued for its age
+anxious - j. causing or fraught with or showing anxiety
+apartheid - n. a social policy or racial segregation involving political and economic and legal discrimination against people who are not Whites
+apocalyptic - j. prophetic of devastation or ultimate doom
+apology - n. an expression of regret at having caused trouble for someone
+apparel - n. clothing in general
+apparent - j. clearly revealed to the mind or the senses or judgment
+appease - v. make peace with
+appropriate - j. suitable for a particular person or place or condition etc
+apt - j. being of striking appropriateness and pertinence
+arbitrary - j. based on or subject to individual discretion or preference or sometimes impulse or caprice
+arcade - n. a covered passageway with shops and stalls on either side
+arrange - v. put into a proper or systematic order
+arrangement - n. an orderly grouping (of things or persons) considered as a unit; the result of arranging
+arrival - n. the act of arriving at a certain place
+arrogance - n. overbearing pride evidenced by a superior manner toward inferiors
+arrogant - j. having or showing feelings of unwarranted importance out of overbearing pride
+articulate - j. expressing yourself easily or characterized by clear expressive language
+assassination - n. murder of a public figure by surprise attack
+assess - v. set or determine the amount of (a payment such as a fine)
+assets - n. anything of material value or usefulness that is owned by a person or company
+asylum - n. a shelter from danger or hardship
+auburn - j. (of hair) colored a moderate reddish-brown
+august - j. profoundly honored
+aura - n. a distinctive but intangible quality surrounding a person or thing
+austere - j. of a stern or strict bearing or demeanor; forbidding in aspect
+authentic - j. not counterfeit or copied
+authenticity - n. undisputed credibility
+authoritarian - n. a person who behaves in a tyrannical manner
+autobiography - n. a biography of yourself
+autonomous - j. existing as an independent entity
+autonomy - n. immunity from arbitrary exercise of authority: political independence
+avid - j. marked by active interest and enthusiasm
+banal - j. repeated too often; over familiar through overuse
+barring - n. the act of excluding someone by a negative vote or veto
+bass - n. the lowest adult male singing voice
+batter - n. a liquid or semiliquid mixture, as of flour, eggs, and milk, used in cooking
+belle - n. a young woman who is the most charming and beautiful of several rivals
+beneficial - j. promoting or enhancing well-being
+benefit - n. something that aids or promotes well-being
+benign - j. not dangerous to health; not recurrent or progressive (especially of a tumor)
+bid - n. a formal proposal to buy at a specified price
+biography - n. an account of the series of events making up a person's life
+biology - n. the science that studies living organisms
+blaze - n. a strong flame that burns brightly
+bleak - j. unpleasantly cold and damp
+bogus - j. fraudulent; having a misleading appearance
+bolster - v. support and strengthen
+bomb - n. an explosive device fused to explode under specific conditions
+bore - n. a person who evokes boredom
+botanical - j. of or relating to plants or botany
+boycott - n. a group's refusal to have commercial dealings with some organization in protest against its policies
+brass - n. an alloy of copper and zinc
+breach - n. an opening (especially a gap in a dike or fortification)
+broadcast - n. message that is transmitted by radio or television
+brokerage - n. the business of a broker; charges a fee to arrange a contract between two parties
+buffet - n. a meal set out on a buffet at which guests help themselves
+bumper - n. a mechanical device consisting of bars at either end of a vehicle to absorb shock and prevent serious damage
+bureau - n. an administrative unit of government
+bureaucracy - n. non elected government officials
+butt - v. to strike, thrust or shove against
+cabinet - n. persons appointed by a head of state to head executive departments of government and act as official advisers
+caliber - n. a degree or grade of excellence or worth
+campaign - n. a series of actions advancing a principle or tending toward a particular end
+canon - n. a rule or especially body of rules or principles generally established as valid and fundamental in a field or art or philosophy
+cardinal - j. serving as an essential component
+caricature - n. a representation of a person that is exaggerated for comic effect
+casual - j. without or seeming to be without plan or method; offhand
+catastrophe - n. an event resulting in great loss and misfortune
+caucus - n. a closed political meeting
+causal - j. involving or constituting a cause; causing
+censure - n. harsh criticism or disapproval
+census - n. a periodic count of the population
+cereal - n. grass whose starchy grains are used as food: wheat; rice; rye; oats; maize; buckwheat; millet
+ceremonial - j. marked by pomp or ceremony or formality
+chaos - n. a state of extreme confusion and disorder
+characteristic - n. a distinguishing quality
+chronic - j. being long-lasting and recurrent or characterized by long suffering
+citadel - n. a stronghold into which people could go for shelter during a battle
+cite - v. refer to for illustration or proof
+clumsy - j. not elegant or graceful in expression
+coalition - n. an organization of people (or countries) involved in a pact or treaty
+coherent - j. marked by an orderly, logical, and aesthetically consistent relation of parts
+coincidence - n. the temporal property of two things happening at the same time
+collapse - v. break down, literally or metaphorically
+colleague - n. an associate that one works with
+collective - j. set up on the principle of collectivism or ownership and production by the workers involved usually under the supervision of a government
+collector - n. a person who collects things
+collision - n. an accident resulting from violent impact of a moving object
+commemorate - v. mark by some ceremony or observation
+commentary - n. a written explanation or criticism or illustration that is added to a book or other textual material
+commission - n. a special group delegated to consider some matter
+commitment - n. the act of binding yourself (intellectually or emotionally) to a course of action
+commodity - n. articles of commerce
+commute - n. a regular journey of some distance to and from your place of work
+comparable - j. able to be compared or worthy of comparison
+comparison - n. the act of examining resemblances
+compassionate - j. showing or having compassion
+compensate - v. make payment to; compensate
+competence - n. the quality of being adequately or well qualified physically and intellectually
+competent - j. properly or sufficiently qualified or capable or efficient
+competitive - j. involving competition or competitiveness
+competitor - n. the contestant you hope to defeat
+complex - j. complicated in structure; consisting of interconnected parts
+component - n. an artifact that is one of the individual parts of which a composite entity is made up; especially a part that can be separated from or attached to a system
+composer - n. someone who composes music as a profession
+comprehensive - j. broad in scope
+concede - v. admit (to a wrongdoing)
+conceive - v. have the idea for
+concession - n. a point conceded or yielded
+confederate - j. united in a group or league
+confidence - n. a state of confident hopefulness that events will be favorable
+confident - j. having or marked by confidence or assurance
+confront - v. oppose, as in hostility or a competition
+conscience - n. a feeling of shame when you do something immoral
+conscious - j. knowing and perceiving; having awareness of surroundings and sensations and thoughts
+consecutive - j. one after the other
+consensus - n. agreement in the judgment or opinion reached by a group as a whole
+conservatism - n. a political or theological orientation advocating the preservation of the best in society and opposing radical changes
+conservative - j. avoiding excess
+consistency - n. a harmonious uniformity or agreement among things or parts
+conspicuous - j. obvious to the eye or mind
+conspiracy - n. a plot to carry out some harmful or illegal act (especially a political plot)
+constituency - n. the body of voters who elect a representative for their area
+consume - v. use up (resources or materials)
+consumer - n. a person who uses goods or services
+consumption - n. the process of taking food into the body through the mouth
+contemplate - v. look at thoughtfully; observe deep in thought
+contemporary - j. belonging to the present time
+contender - n. the contestant you hope to defeat
+contentious - j. inclined or showing an inclination to dispute or disagree, even to engage in law suits
+contingent - j. possible but not certain to occur
+continuous - j. continuing in time or space without interruption
+contradiction - n. opposition between two conflicting forces or ideas
+contradictory - j. unable for both to be true at the same time
+contribution - n. a voluntary gift (as of money or service or ideas) made to some worthwhile cause
+contributor - n. someone who contributes (or promises to contribute) a sum of money
+convenience - n. the quality of being useful and convenient
+conversion - n. the act of changing from one use or function or purpose to another
+convertible - j. designed to be changed from one use or form to another
+conviction - n. an unshakable belief in something without need for proof or evidence
+corporate - j. of or belonging to a corporation
+corps - n. a body of people associated together
+corruption - n. destroying someone's (or some group's) honesty or loyalty; undermining moral integrity
+cosmetic - j. serving an esthetic rather than a useful purpose
+cosmopolitan - j. of worldwide scope or applicability
+counsel - n. something that provides direction or advice as to a decision or course of action
+counterpart - n. a person or thing having the same function or characteristics as another
+courageous - j. able to face and deal with danger or fear without flinching
+course - n. a connected series of events or actions or developments
+courtesy - n. a courteous or respectful or considerate act
+credible - j. appearing to merit belief or acceptance
+critique - n. a serious examination and judgment of something
+crusade - v. exert oneself continuously, vigorously, or obtrusively to gain an end or engage in a crusade for a certain cause or person; be an advocate for
+crush - v. to compress with violence, out of natural shape or condition
+curator - n. the custodian of a collection (as a museum or library)
+curriculum - n. an integrated course of academic studies
+cynical - j. believing the worst of human nature and motives; having a sneering disbelief in e.g. selflessness of others
+cynicism - n. a cynical feeling of distrust
+daring - n. the trait of being willing to undertake things that involve risk or danger
+debacle - n. a sudden and violent collapse
+debut - v. appear for the first time in public
+decay - n. the organic phenomenon of rotting
+decency - n. the quality of conforming to standards of propriety and morality
+decent - j. socially or conventionally correct; refined or virtuous
+decisive - j. characterized by decision and firmness
+decree - n. a legally binding command or decision entered on the court record (as if issued by a court or judge)
+dedication - n. complete and wholehearted fidelity
+default - n. loss due to not showing up
+defendant - n. a person or institution against whom an action is brought in a court of law; the person being sued or accused
+defensive - j. attempting to justify or defend in speech or writing
+defiance - n. a hostile challenge
+definite - j. known for certain
+delicacy - n. something considered choice to eat
+demise - v. transfer by a lease or by a will
+demonstrate - v. establish the validity of something, as by an example, explanation or experiment
+denominator - n. the divisor of a fraction
+deposition - n. (law) a pretrial interrogation of a witness; usually conducted in a lawyer's office
+depression - n. a long-term economic state characterized by unemployment and low prices and low levels of trade and investment
+depth - n. the attribute or quality of being deep, strong, or intense
+derive - v. come from
+descent - n. properties attributable to your ancestry
+desert - n. arid land with little or no vegetation
+designate - v. give an assignment to (a person) to a post, or assign a task to (a person)
+despair - n. a state in which all hope is lost or absent
+desperate - j. showing extreme urgency or intensity especially because of great need or desire
+detect - v. discover or determine the existence, presence, or fact of
+deter - v. try to prevent; show opposition to
+determination - n. the quality of being determined to do or achieve something; firmness of purpose
+deterrent - j. tending to deter
+diagnosis - n. identifying the nature or cause of some phenomenon
+dialogue - n. a discussion intended to produce an agreement
+difference - n. the quality of being unlike or dissimilar
+dignity - n. the quality of being worthy of esteem or respect
+dilemma - n. state of uncertainty or perplexity especially as requiring a choice between equally unfavorable options
+diplomacy - n. subtly skillful handling of a situation
+diplomat - n. a person who deals tactfully with others
+diplomatic - j. using or marked by tact in dealing with sensitive matters or people
+disagree - v. be of different opinions
+disappear - v. become invisible or unnoticeable
+discern - v. detect with the senses
+discipline - n. a system of rules of conduct or method of practice
+discomfort - n. the state of being tense and feeling pain
+discourse - n. extended verbal expression in speech or writing
+discover - v. see for the first time; make a discovery
+discriminate - v. treat differently on the basis of sex or race
+discussion - n. an exchange of views on some topic
+disdain - n. lack of respect accompanied by a feeling of intense dislike
+dishonest - j. deceptive or fraudulent; disposed to cheat or defraud or deceive
+dismal - j. causing dejection
+dismay - v. fill with apprehension or alarm; cause to be unpleasantly surprised
+dismissal - n. the termination of someone's employment (leaving them free to depart)
+disparity - n. inequality or difference in some respect
+disregard - n. willful lack of care and attention
+dissent - n. a difference of opinion
+distant - j. separated in space or coming from or going to a distance
+distinction - n. a distinguishing difference
+distress - n. extreme physical pain
+distrust - v. regard as untrustworthy; regard with suspicion; have no faith or confidence in
+diverse - j. many and different
+diversion - n. an activity that diverts or amuses or stimulates
+diversity - n. noticeable heterogeneity
+divert - v. turn aside; turn away from
+document - n. writing that provides information (especially information of an official nature)
+doe - n. mature female of mammals of which the male is called 'buck'
+domain - n. territory over which rule or control is exercised
+dominance - n. the state that exists when one person or group has power over another
+dominant - j. exercising influence or control
+dominate - v. be in control
+domination - n. social control by dominating
+donate - v. give to a charity or good cause
+donor - n. person who makes a gift of property
+drastic - j. forceful and extreme and rigorous
+drought - n. a shortage of rainfall
+dubious - j. open to doubt or suspicion
+dynamics - n. the branch of mechanics concerned with the forces that cause motions of bodies
+earnest - j. characterized by a firm and humorless belief in the validity of your opinions
+eccentric - n. a person with an unusual or odd personality
+eclectic - j. selecting what seems best of various styles or ideas
+editorial - n. an article giving opinions or perspectives
+effect - n. a phenomenon that follows and is caused by some previous phenomenon
+effective - j. works well as a means or remedy
+efficiency - n. skillfulness in avoiding wasted time and effort
+efficient - j. able to accomplish a purpose; functioning effectively
+elaborate - v. add details, as to an account or idea; clarify the meaning of and discourse in a learned way, usually in writing
+element - n. any of the more than 100 known substances (of which 92 occur naturally) that cannot be separated into simpler substances and that singly or in combination constitute all matter
+eligible - j. qualified for or allowed or worthy of being chosen
+eliminate - v. terminate, end, or take out
+embargo - n. a government order imposing a trade barrier
+emblem - n. a visible symbol representing an abstract idea
+embrace - n. the act of clasping another person in the arms (as in greeting or affection)
+emerge - v. come out into view, as from concealment
+emergence - n. the gradual beginning or coming forth
+eminent - j. of imposing height; especially standing out above others
+emphasis - n. intensity or forcefulness of expression
+emphasize - v. to stress, single out as important
+employee - n. a worker who is hired to perform a job
+employer - n. a person or firm that employs workers
+emulate - v. imitate the function of (another system), as by modifying the hardware or the software
+enact - v. order by virtue of superior authority; decree
+encourage - v. inspire with confidence; give hope or courage to
+encyclopedia - n. a reference work (often in several volumes) containing articles on various topics
+endorse - v. be behind; approve of
+enduring - j. patiently bearing continual wrongs or trouble
+energetic - j. possessing or exerting or displaying energy
+enhance - v. make better or more attractive
+enormous - j. extraordinarily large in size or extent or amount or power or degree
+enthusiastic - j. having or showing great excitement and interest
+entity - n. that which is perceived or known or inferred to have its own distinct existence (living or nonliving)
+epic - j. very imposing or impressive; surpassing the ordinary (especially in size or scale)
+epidemic - n. a widespread outbreak of an infectious disease; many people are infected at the same time
+episode - n. a brief section of a literary or dramatic work that forms part of a connected series
+equilibrium - n. a stable situation in which forces cancel one another
+equity - n. the difference between the market value of a property and the claims held against it
+equivalent - j. being essentially equal to something
+error - n. a wrong action attributable to bad judgment or ignorance or inattention
+esquire - n. a title of respect for a member of the English gentry ranking just below a knight; placed after the name
+essence - n. the central meaning or theme of a speech or literary work
+evangelical - j. relating to or being a Christian church believing in personal conversion and the inerrancy of the Bible especially the 4 Gospels
+evoke - v. call to mind
+evolution - n. a process in which something passes by degrees to a different stage (especially a more advanced or mature stage)
+exceed - v. be greater in scope or size than some standard
+excellent - j. very good; of the highest quality
+excerpt - n. a passage selected from a larger work
+excess - j. more than is needed, desired, or required
+exclude - v. prevent from being included or considered or accepted
+excursion - n. a journey taken for pleasure
+exempt - v. grant relief or an exemption from a rule or requirement to
+existence - n. everything that exists anywhere
+exit - v. move out of or depart from
+exotic - j. strikingly strange or unusual
+expand - v. become larger in size or volume or quantity
+expansion - n. the act of increasing (something) in size or volume or quantity or scope
+expect - v. regard something as probable or likely
+expectancy - n. something expected (as on the basis of a norm)
+expense - n. money spent to perform work and usually reimbursed by an employer
+expertise - n. skillfulness by virtue of possessing special knowledge
+explicit - j. precisely and clearly expressed or readily observable; leaving nothing to implication
+explode - v. drive from the stage by noisy disapproval
+exploit - v. use or manipulate to one's advantage
+explosion - n. the act of exploding or bursting
+explosive - n. a chemical substance that undergoes a rapid chemical change (with the production of gas) on being heated or struck
+exposure - n. vulnerability to the elements; to the action of heat or cold or wind or rain
+expressive - j. characterized by expression
+extension - n. an addition to the length of something
+extensive - j. large in spatial extent or range or scope or quantity
+exterior - n. the outer side or surface of something
+external - j. happening or arising or located outside or beyond some limits or especially surface
+extradition - n. the surrender of an accused or convicted person by one state or country to another (usually under the provisions of a statute or treaty)
+extraordinary - j. beyond what is ordinary or usual; highly unusual or exceptional or remarkable
+extravagant - j. unrestrained, especially with regard to feelings
+exuberant - j. joyously unrestrained
+fabulous - j. extremely pleasing
+facial - j. of or concerning the face
+facility - n. something designed and created to serve a particular function and to afford a particular convenience or service
+faction - n. a dissenting clique
+faulty - j. characterized by errors; not agreeing with a model or not following established rules
+feasible - j. capable of being done with means at hand and circumstances as they are
+felony - n. a serious crime (such as murder or arson)
+feminine - j. associated with women and not with men
+fervor - n. the state of being emotionally aroused and worked up
+fetus - n. an unborn or unhatched vertebrate in the later stages of development showing the main recognizable features of the mature animal
+feud - n. a bitter quarrel between two parties
+feudal - j. of or relating to or characteristic of feudalism
+fidelity - n. the quality of being faithful
+finale - n. the concluding part of any performance
+finite - j. bounded or limited in magnitude or spatial or temporal extent
+fiscal - j. involving financial matters
+flag - n. emblem usually consisting of a rectangular piece of cloth of distinctive design
+flamboyant - j. marked by ostentation but often tasteless
+fleet - n. a group of warships organized as a tactical unit
+flexible - j. able to flex; able to bend easily
+flop - n. a complete failure
+flourish - n. a showy gesture
+foil - n. anything that serves by contrast to call attention to another thing's good qualities
+ford - v. cross a river where it's shallow
+forecast - n. a prediction about how something (as the weather) will develop
+foreign - j. relating to or originating in or characteristic of another place or part of the world
+foresee - v. act in advance of; deal with ahead of time
+formation - n. an arrangement of people or things acting as a unit
+formidable - j. extremely impressive in strength or excellence
+formula - n. directions for making something
+forte - n. an asset of special worth or utility
+forth - a. forward in time or order or degree
+foster - v. promote the growth of
+fragile - j. easily broken or damaged or destroyed
+frantic - j. excessively agitated; distraught with fear or other violent emotion
+fray - v. wear away by rubbing
+frequency - n. the number of occurrences within a given time period
+fringe - n. a social group holding marginal or extreme views
+frivolous - j. not serious in content or attitude or behavior
+frontier - n. a wilderness at the edge of a settled area of a country
+fundamental - j. being or involving basic facts or principles
+further - v. promote the growth of
+futile - j. producing no result or effect
+galaxy - n. (astronomy) a collection of star systems; any of the billions of systems each having many stars and nebulae and dust
+gamble - v. take a risk in the hope of a favorable outcome
+gauge - n. a measuring instrument for measuring and indicating a quantity such as the thickness of wire or the amount of rain etc.
+generate - v. bring into existence
+generic - j. applicable to an entire class or group
+generosity - n. the trait of being willing to give your money or time
+genesis - n. the first book of the Old Testament: tells of Creation; Adam and Eve; Cain and Abel
+gesture - n. motion of hands or body to emphasize or help to express a thought or feeling
+gigantic - j. so exceedingly large or extensive as to suggest a giant or mammoth
+gist - n. the choicest or most essential or most vital part of some idea or experience
+glimpse - n. a brief or incomplete view
+glorious - j. having great beauty and splendor
+grandeur - n. the quality of being magnificent or splendid or grand
+grandiose - j. impressive because of unnecessary largeness or grandeur; used to show disapproval
+grave - j. of great gravity or crucial import; requiring serious thought
+gravity - n. a manner that is serious and solemn
+grief - n. something that causes great unhappiness
+grotesque - j. distorted and unnatural in shape or size; abnormal and hideous
+grove - n. a small growth of trees without underbrush
+guise - n. an artful or simulated semblance
+hack - n. a mediocre and disdained writer
+hale - j. exhibiting or restored to vigorous good health
+handwriting - n. something written by hand
+harbor - v. hold back a thought or feeling about
+hazard - n. a source of danger; a possibility of incurring loss or misfortune
+heir - n. a person who is entitled by law or by the terms of a will to inherit the estate of another
+heritage - n. practices that are handed down from the past by tradition
+hilarious - j. marked by or causing boisterous merriment or convulsive laughter
+hollow - j. not solid; having a space or gap or cavity
+homage - n. respectful deference
+hostility - n. violent action that is hostile and usually unprovoked
+humane - j. marked or motivated by concern with the alleviation of suffering
+humanitarian - n. someone devoted to the promotion of human welfare and to social reforms
+hush - v. become quiet or still; fall silent
+hybrid - n. (genetics) an organism that is the offspring of genetically dissimilar parents or stock; especially offspring produced by breeding plants or animals of different varieties or breeds or species
+hypocrisy - n. insincerity by virtue of pretending to have qualities or beliefs that you do not really have
+hypothesis - n. a tentative insight into the natural world; a concept that is not yet verified but that if true would explain certain facts or phenomena
+hysteria - n. excessive or uncontrollable fear
+icon - n. a conventional religious painting in oil on a small wooden panel; venerated in the Eastern Church
+ideology - n. an orientation that characterizes the thinking of a group or nation
+illusion - n. an erroneous mental representation
+imaginary - j. not based on fact; unreal
+imitation - n. something copied or derived from an original
+immense - j. unusually great in size or amount or degree or especially extent or scope
+immigrant - n. a person who comes to a country where they were not born in order to settle there
+imminent - j. close in time; about to occur
+immoral - j. not adhering to ethical or moral principles
+immune - j. (usually followed by 'to') not affected by a given influence
+impending - j. close in time; about to occur
+implausible - j. having a quality that provokes disbelief
+implicit - j. implied though not directly expressed; inherent in the nature of something
+imply - v. express or state indirectly
+impose - v. compel to behave in a certain way
+improper - j. not suitable or right or appropriate
+impulse - n. a sudden desire
+inadequate - j. not sufficient to meet a need
+incentive - n. a positive motivational influence
+incidence - n. the relative frequency of occurrence of something
+incident - n. a public disturbance
+incidentally - a. of a minor or subordinate nature
+inclined - j. at an angle to the horizontal or vertical position
+incompetence - n. lack of physical or intellectual ability or qualifications
+inconsistent - j. displaying a lack of consistency
+inconvenient - j. not suited to your comfort, purpose or needs
+indefinitely - a. to an indefinite extent; for an indefinite time
+indicator - n. a device for showing the operating condition of some system
+indifferent - j. showing no care or concern in attitude or action
+indigenous - j. originating where it is found
+indulge - v. enjoy to excess
+inefficient - j. not producing desired results; wasteful
+inept - j. generally incompetent and ineffectual
+inevitable - j. incapable of being avoided or prevented
+inexpensive - j. relatively low in price or charging low prices
+infamous - j. known widely and usually unfavorably
+infinite - j. having no limits or boundaries in time or space or extent or magnitude
+influence - n. a power to affect persons or events especially power based on prestige etc
+influential - j. having or exercising influence or power
+influx - n. the process of flowing in
+ingenious - j. showing inventiveness and skill
+inherent - j. in the nature of something though not readily apparent
+injunction - n. (law) a judicial remedy issued in order to prohibit a party from doing or continuing to do a certain activity
+inland - a. towards or into the interior of a region
+insight - n. the clear (and often sudden) understanding of a complex situation
+insistence - n. the state of demanding notice or attention
+inspector - n. a high ranking police officer
+instance - n. an occurrence of something
+instant - n. a very short time
+insufficient - j. of a quantity not able to fulfill a need or requirement
+integral - j. essential to completeness; lacking nothing
+integrity - n. moral soundness
+intellectual - j. appealing to or using the intellect
+intelligence - n. the ability to comprehend; to understand and profit from experience
+intensive - j. characterized by a high degree or intensity; often used as a combining form
+intention - n. an act of intending; a volition that you intend to carry out
+interact - v. act together or towards others or with others
+interim - n. the time between one event, process, or period and another
+intermediate - j. lying between two extremes in time or space or state
+intervene - v. get involved, so as to alter or hinder an action, or through force or threat of force
+intervention - n. the act of intervening (as to mediate a dispute, etc.)
+intimacy - n. close or warm friendship
+intricate - j. having many complexly arranged elements; elaborate
+invasion - n. any entry into an area not previously occupied
+inventive - j. (used of persons or artifacts) marked by independence and creativity in thought or action
+investigator - n. a police officer who investigates crimes
+investor - n. someone who commits capital in order to gain financial returns
+invincible - j. incapable of being overcome or subdued
+invoke - v. cite as an authority; resort to
+involuntary - j. not subject to the control of the will
+involve - v. engage as a participant
+irony - n. incongruity between what might be expected and what actually occurs
+irrational - j. not consistent with or using reason
+irrelevant - j. having no bearing on or connection with the subject at issue
+irresistible - j. impossible to resist; overpowering
+irresponsible - j. showing lack of care for consequences
+judgment - n. the capacity to assess situations or circumstances shrewdly and to draw sound conclusions
+judicial - j. belonging or appropriate to the office of a judge
+juicy - j. lucrative
+junction - n. something that joins or connects
+jurisdiction - n. (law) the right and power to interpret and apply the law
+juror - n. someone who serves (or waits to be called to serve) on a jury
+justification - n. something (such as a fact or circumstance) that shows an action to be reasonable or necessary
+juvenile - j. of or relating to or characteristic of or appropriate for children or young people
+ken - n. range of what one can know or understand
+knight - n. originally a person of noble birth trained to arms and chivalry; today in Great Britain a person honored by the sovereign for personal merit
+knit - n. needlework created by interlacing yarn in a series of connected loops using straight eyeless needles or by machine
+lament - v. regret strongly
+landmark - n. the position of a prominent or well-known object in a particular landscape
+landscape - n. an expanse of scenery that can be seen in a single view
+lapse - n. a break or intermission in the occurrence of something
+laureate - n. someone honored for great achievements; figuratively someone crowned with a laurel wreath
+lavish - j. very generous
+lax - j. lacking in rigor or strictness
+legacy - n. (law) a gift of personal property by will
+legislative - j. relating to a legislature or composed of members of a legislature
+legitimacy - n. lawfulness by virtue of being authorized or in accordance with law
+legitimate - j. in accordance with recognized or accepted standards or principles
+leisure - n. time available for ease and relaxation
+lenient - j. not strict
+levy - v. impose and collect
+liable - j. held legally responsible
+liberalism - n. a political orientation that favors social progress by reform and by changing laws rather than by revolution
+lifelong - j. continuing through life
+lifetime - n. the period during which something is functional (as between birth and death)
+likelihood - n. the probability of a specified outcome
+liking - n. a feeling of pleasure and enjoyment
+liquor - n. an alcoholic beverage that is distilled rather than fermented
+literacy - n. the ability to read and write
+literal - j. avoiding embellishment or exaggeration (used for emphasis)
+literature - n. creative writing of recognized artistic value
+logic - n. the principles that guide reasoning within a given field or situation
+logical - j. capable of thinking and expressing yourself in a clear and consistent manner
+lovable - j. having characteristics that attract love or affection
+lucrative - j. producing a sizeable profit
+ludicrous - j. broadly or extravagantly humorous; resembling farce
+lying - n. the deliberate act of deviating from the truth
+machinery - n. machines or machine systems collectively
+magnet - n. (physics) a device that attracts iron and produces a magnetic field
+magnificent - j. characterized by grandeur
+magnitude - n. the property of relative size or extent (whether large or small)
+maintain - v. state or assert
+maintenance - n. activity involved in maintaining something in good working order
+makeup - n. the way in which someone or something is composed
+mandate - n. an authorization to act given to a representative
+mandatory - j. required by rule
+maneuver - v. act in order to achieve a certain goal
+manifesto - n. a public declaration of intentions (as issued by a political party or government)
+marine - j. native to or inhabiting the sea
+maritime - j. relating to or involving ships or shipping or navigation or seamen
+martial - j. suggesting war or military life
+marvel - v. be amazed at
+massacre - n. the savage and excessive killing of many people
+massive - j. imposing in size or bulk or solidity
+masterpiece - n. the most outstanding work of a creative artist or craftsman
+material - j. concerned with worldly rather than spiritual interests
+maternal - j. relating to or derived from one's mother
+maze - n. complex system of paths or tunnels in which it is easy to get lost
+mechanics - n. the technical aspects of doing something
+medicine - n. something that treats or prevents or alleviates the symptoms of disease
+medieval - j. as if belonging to the Middle Ages; old-fashioned and unenlightened
+mediocre - j. moderate to inferior in quality
+meditation - n. continuous and profound contemplation or musing on a subject or series of subjects of a deep or abstruse nature
+melodrama - n. an extravagant comedy in which action is more salient than characterization
+memorable - j. worth remembering
+menace - v. act in a threatening manner
+mentality - n. a habitual or characteristic mental attitude that determines how you will interpret and respond to situations
+mentor - v. serve as a teacher or trusted counselor
+metal - n. any of several chemical elements that are usually shiny solids
+metaphor - n. a figure of speech in which an expression is used to refer to something that it does not literally denote in order to suggest a similarity
+metric - j. based on the meter as a standard of measurement
+metropolis - n. a large and densely populated urban area; may include several independent administrative districts
+metropolitan - j. relating to or characteristic of a densely populated urban area
+mileage - n. the ratio of the number of miles traveled to the number of gallons of gasoline burned; miles per gallon
+militant - j. disposed to warfare or hard-line policies
+militia - n. civilians trained as soldiers but not part of the regular army
+miniature - j. being on a very small scale
+minimize - v. make small or insignificant
+ministry - n. a government department under the direction of a minister
+minority - n. being or relating to the smaller in number of two parts
+minute - j. infinitely or immeasurably small
+misdemeanor - n. a crime less serious than a felony
+missile - n. a rocket carrying a warhead of conventional or nuclear explosives
+momentum - n. an impelling force or strength
+monarchy - n. an autocracy governed by a monarch who usually inherits the authority
+monastery - n. the residence of a religious community
+monetary - j. relating to or involving money
+monopoly - n. (economics) a market in which there are many buyers but only one seller
+morale - n. the spirit of a group that makes the members want the group to succeed
+morality - n. concern with the distinction between good and evil or right and wrong; right or good conduct
+motto - n. a favorite saying of a sect or political group
+mundane - j. concerned with the world or worldly matters; ordinary
+municipal - j. relating to city government
+muster - v. gather or bring together
+myriad - n. a large indefinite number
+myth - n. a traditional story accepted as history; serves to explain the world view of a people
+mythology - n. myths collectively; the body of stories associated with a culture or institution or person
+narrative - n. a message that tells the particulars of an act or occurrence or course of events; presented in writing or drama or cinema or as a radio or television program
+narrator - n. someone who tells a story
+naturally - a. according to nature; by natural means; without artificial help
+naval - j. connected with or belonging to or used in a navy
+necessary - j. absolutely essential
+necessity - n. anything indispensable
+network - n. an interconnected system of things or people
+neutral - j. having no personal preference
+nevertheless - a. despite anything to the contrary (usually following a concession)
+noisy - j. full of or characterized by loud and nonmusical sounds
+nomination - n. the condition of having been proposed as a suitable candidate for appointment or election
+nominee - n. a politician who is running for public office
+norm - n. a standard or model or pattern regarded as typical
+notorious - j. known widely and usually unfavorably
+nude - n. without clothing (especially in the phrase 'in the nude')
+obesity - n. more than average fatness
+objective - j. undistorted by emotion or personal bias; based on observable phenomena
+observatory - n. a building designed and equipped to observe astronomical phenomena
+obsolete - j. no longer in use
+obstruction - n. something that stands in the way and must be circumvented or surmounted
+obtain - v. come into possession of
+occasion - n. a vaguely specified social event
+odor - n. the sensation that results when olfactory receptors in the nose are stimulated by particular chemicals in gaseous form
+ominous - j. threatening or foreshadowing evil or tragic developments
+operate - v. handle and cause to function
+operator - n. an agent that operates some apparatus or machine
+opinion - n. a personal belief or judgment that is not founded on proof or certainty
+opponent - n. a contestant that you are matched against
+opportunity - n. a possibility due to a favorable combination of circumstances
+optimism - n. a general disposition to expect the best in all things
+option - n. one of a number of things from which only one can be chosen
+oral - j. of or relating to or affecting or for use in the mouth
+ordeal - n. a severe or trying experience
+ornate - j. marked by elaborate rhetoric and elaborated with decorative details
+orthodox - j. adhering to what is commonly accepted
+outbreak - n. a sudden violent spontaneous occurrence (usually of some undesirable condition)
+outcry - n. a loud utterance; often in protest or opposition
+outrage - n. a feeling of righteous anger
+outrageous - j. grossly offensive to decency or morality; causing horror
+outright - a. without reservation or concealment
+overhaul - v. make repairs, renovations, revisions or adjustments to
+oversee - v. watch and direct
+overthrow - n. the termination of a ruler or institution (especially by force)
+overweight - n. the property of excessive fatness
+pact - n. a written agreement between two states or sovereigns
+pageant - n. a rich and spectacular ceremony
+panic - n. an overwhelming feeling of fear and anxiety
+pantheon - n. all the gods of a religion
+paradox - n. (logic) a statement that contradicts itself
+parallel - j. being everywhere equidistant and not intersecting
+parish - n. a local church community
+parliament - n. a legislative assembly in certain countries
+parody - v. make a spoof of or make fun of
+participant - n. someone who takes part in an activity
+participate - v. become a participant; be involved in
+partisan - n. an ardent and enthusiastic supporter of some person or activity
+partition - v. divide into parts, pieces, or sections
+passive - j. lacking in energy or will
+patriotism - n. love of country and willingness to sacrifice for it
+patron - n. someone who supports or champions something
+pavilion - n. large and often sumptuous tent
+peaceful - j. not disturbed by strife or turmoil or war
+pedestrian - j. lacking wit or imagination
+penalty - n. a punishment for a crime or offense
+penchant - n. a strong liking
+pennant - n. a long flag; often tapering
+pension - n. a regular payment to a person that is intended to allow them to subsist without working
+pentagon - n. a five-sided polygon
+perceive - v. to become aware of or conscious of
+perception - n. becoming aware of something via the senses
+perennial - j. lasting an indefinitely long time; suggesting self-renewal
+perform - v. carry out or perform an action
+perjury - n. criminal offense of making false statements under oath
+permanent - j. continuing or enduring without marked change in status or condition or place
+perpetual - j. continuing forever or indefinitely
+persist - v. be persistent, refuse to stop
+personal - j. concerning or affecting a particular person or his or her private life and personality
+personality - n. the complex of all the attributes--behavioral, temperamental, emotional and mental--that characterize a unique individual
+personnel - n. persons collectively in the employ of a business
+perspective - n. the appearance of things relative to one another as determined by their distance from the viewer
+persuade - v. cause somebody to adopt a certain position, belief, or course of action; twist somebody's arm
+pervasive - j. spreading or spread throughout
+petty - j. contemptibly narrow in outlook
+phenomenal - j. exceedingly or unbelievably great
+phenomenon - n. any state or process known through the senses rather than by intuition or reasoning
+philharmonic - j. composing or characteristic of an orchestral group
+philosophy - n. any personal belief about how to live or how to deal with a situation
+physicist - n. a scientist trained in physics
+physics - n. the science of matter and energy and their interactions
+pinch - n. a squeeze with the fingers
+pine - n. straight-grained white to yellowish tree
+pioneer - v. open up and explore a new area
+pivotal - j. being of crucial importance
+plausible - j. apparently reasonable and valid, and truthful
+playful - j. full of fun and high spirits
+playwright - n. someone who writes plays
+plea - n. a humble request for help from someone in authority
+plead - v. appeal or request earnestly
+pleasant - j. affording pleasure; being in harmony with your taste or likings
+plunge - v. fall abruptly
+poetic - j. of or relating to poetry
+poignant - j. arousing emotions; touching
+poised - j. in full control of your faculties
+portfolio - n. a set of pieces of creative work collected to be shown to potential customers or employers
+positive - j. characterized by or displaying affirmation or acceptance or certainty etc.
+possess - v. have as an attribute, knowledge, or skill
+possession - n. the act of having and controlling property
+potent - j. having a strong physiological or chemical effect
+potential - j. existing in possibility
+precedent - n. an example that is used to justify similar occurrences at a later time
+precise - j. sharply exact or accurate or delimited
+precision - n. the quality of being reproducible in amount or performance
+predecessor - n. one who precedes you in time (as in holding a position or office)
+predict - v. make a prediction about; tell in advance
+prediction - n. a statement made about the future
+prefer - v. like better; value more highly
+preference - n. a strong liking
+prejudice - n. a partiality that prevents objective consideration of an issue or situation
+premature - j. too soon or too hasty
+premier - v. be performed for the first time
+premise - v. set forth beforehand, often as an explanation
+preparation - n. the activity of putting or setting in order in advance of some act or purpose
+preposterous - j. incongruous; inviting ridicule
+prescription - n. written instructions from a physician or dentist to a druggist concerning the form and dosage of a drug to be issued to a given patient
+preservation - n. the activity of protecting something from loss or danger
+pretentious - j. making claim to or creating an appearance of (often undeserved) importance or distinction
+prevalent - j. most frequent or common
+prevention - n. the act of preventing
+primer - n. an introductory textbook
+primitive - j. belonging to an early stage of technical development; characterized by simplicity and (often) crudeness
+principal - n. the educator who has executive authority for a school
+principle - n. a basic truth or law or assumption
+principled - j. based on or manifesting objectively defined standards of rightness or morality
+pristine - j. completely free from dirt or contamination
+privilege - n. a special advantage or immunity or benefit not enjoyed by all
+probation - n. (law) a way of dealing with offenders without imprisoning them; a defendant found guilty of a crime is released by the court without imprisonment subject to conditions imposed by the court
+probe - v. question or examine thoroughly and closely
+procedure - n. a process or series of acts especially of a practical or mechanical nature involved in a particular form of work
+proceed - v. move ahead; travel onward in time or space
+productive - j. producing or capable of producing (especially abundantly)
+profession - n. an occupation requiring special education (especially in the liberal arts or sciences)
+professor - n. someone who is a member of the faculty at a college or university
+profile - n. an outline of something (especially a human face as seen from one side)
+progressive - j. favoring or promoting progress
+prohibition - n. the action of prohibiting or inhibiting or forbidding (or an instance thereof)
+prolific - j. bearing in abundance especially offspring
+promenade - n. a public area set aside as a pedestrian walk
+prominence - n. relative importance
+prominent - j. having a quality that thrusts itself into attention
+promoter - n. someone who is an active supporter and advocate
+prone - j. having a tendency (to); often used in combination
+propaganda - n. information that is spread for the purpose of promoting some cause
+prophet - n. someone who speaks by divine inspiration; someone who is an interpreter of the will of God
+protagonist - n. the principal character in a work of fiction
+protection - n. the activity of protecting someone or something
+protective - j. intended or adapted to afford protection of some kind
+protestant - j. of or relating to Protestants or Protestantism
+provincial - j. characteristic of the provinces or their people
+provoke - v. evoke or provoke to appear or occur
+proxy - n. a person authorized to act for another
+prudence - n. knowing how to avoid embarrassment or distress
+psychic - j. outside the sphere of physical science
+pundit - n. someone who has been admitted to membership in a scholarly field
+quake - v. shake with fast, tremulous movements
+qualify - v. make fit or prepared
+quarterly - a. in three month intervals
+radical - j. markedly new or introducing extreme change
+rampant - j. unrestrained and violent
+rapid - j. characterized by speed; moving with or capable of moving with high speed
+rave - v. praise enthusiastically
+reaction - n. a response that reveals a person's feelings or attitude
+readily - a. without much difficulty
+realism - n. the attribute of accepting the facts of life and favoring practicality and literal truth
+recipient - n. a person who receives something
+reckless - j. characterized by careless unconcern
+recognize - v. detect with the senses
+reconcile - v. come to terms
+reconsider - v. consider again; give new consideration to; usually with a view to changing
+recover - v. get or find back; recover the use of
+recruit - v. cause to assemble or enlist in the military
+redemption - n. (theology) the act of delivering from sin or saving from evil
+refer - v. send or direct for treatment, information, or a decision
+reflection - n. the image of something as reflected by a mirror (or other reflective material)
+reform - v. make changes for improvement in order to remove abuse and injustices
+refuge - n. a shelter from danger or hardship
+refusal - n. the act of not accepting something that is offered
+regime - n. the government or governing authority of a political unit
+regional - j. related or limited to a particular region
+reign - v. have sovereign power
+relevant - j. having a bearing on or connection with the subject at issue
+reliant - j. depending on another for support
+reluctance - n. a certain degree of unwillingness
+reluctant - j. not eager
+reminiscent - j. serving to bring to mind
+renaissance - n. the period of European history at the close of the Middle Ages and the rise of the modern world; a cultural rebirth from the 14th through the middle of the 17th centuries
+render - v. give or supply
+renowned - j. widely known and esteemed
+repeal - n. cancel officially
+reproduction - n. the act of making copies
+requisite - n. anything indispensable
+resemblance - n. similarity in appearance or external or superficial details
+resent - v. feel bitter or indignant about
+resist - v. withstand the force of something
+resistance - n. the action of opposing something that you disapprove or disagree with
+resistant - j. impervious to being affected
+resort - v. have recourse to
+resource - n. a source of aid or support that may be drawn upon when needed
+restore - v. bring back into original existence, use, function, or position
+resurrection - n. (New Testament) the rising of Christ on the third day after the Crucifixion
+retrospect - n. contemplation of things past
+retrospective - j. concerned with or related to the past
+revelation - n. communication of knowledge to man by a divine or supernatural agency
+revive - v. be brought back to life, consciousness, or strength
+rhetoric - n. using language effectively to please or persuade
+ridiculous - j. incongruous; absurd; nonsensical
+rigorous - j. demanding strict attention to rules and procedures
+robust - j. sturdy and strong in form, constitution, or construction
+rue - n. sadness associated with some wrong done or some disappointment
+rural - j. living in or characteristic of farming or country life
+rustic - j. characteristic of the fields or country
+sacrifice - v. kill or destroy
+savage - v. criticize harshly or violently
+scholarly - j. characteristic of learning or studying
+scope - n. an area in which something acts or operates or has power or control
+script - n. a written version of a play or other dramatic composition; used in preparing for a performance
+secession - n. formal separation from an alliance or federation
+secondary - j. not of major importance
+secrecy - n. the trait of keeping things secret
+secular - j. not concerned with or devoted to religion
+seize - v. take hold of; grab
+selective - j. characterized by very careful or fastidious choices
+seminar - n. any meeting for an exchange of ideas
+sensation - n. a perception associated with stimulation of a sensory organ
+sensibility - n. refined sensitivity to pleasurable or painful impressions
+sensitive - j. responsive to physical stimuli
+sentence - n. the period of time a prisoner is imprisoned
+sentinel - n. a person employed to keep watch for some anticipated event
+sequel - n. a part added to a book or play that continues and extends it
+sequence - n. serial arrangement in which things follow in logical order or a recurrent pattern
+sergeant - n. any of several noncommissioned officer ranks in the Army or Air Force or Marines ranking above a corporal
+servitude - n. state of subjection to an owner or master or forced labor imposed as punishment
+severely - a. with sternness; in a severe manner
+shallow - j. lacking depth of intellect or knowledge; concerned only with what is obvious
+sheer - j. complete and without restriction or qualification
+shrewd - j. marked by practical hardheaded intelligence
+siege - n. the action of an armed force that surrounds a fortified place and isolates it while continuing to attack
+significance - n. the quality of being important in effect or meaning
+significant - j. important in effect or meaning
+similar - j. having close to the same characteristics
+sinister - j. stemming from evil characteristics or forces; wicked or dishonorable
+skepticism - n. the disbelief in any claims of ultimate knowledge
+slack - j. not tense or taut
+slight - n. a deliberate discourteous act (usually as an expression of anger or disapproval)
+sober - v. become more realistic
+socialism - n. a political theory advocating state ownership of industry
+socialist - j. advocating or following the socialist principles of state ownership
+sociology - n. the study and classification of human societies
+solar - j. relating to or derived from the sun or utilizing the energies of the sun
+soldier - n. an enlisted man or woman who serves in an army
+somber - j. grave or even gloomy in character
+sophisticated - j. having or appealing to those having worldly knowledge and refinement
+souvenir - n. a reminder of past events
+specialty - n. an asset of special worth or utility
+species - n. (biology) taxonomic group whose members can interbreed
+spectator - n. a close observer; someone who looks at something (such as an exhibition of some kind)
+specter - n. a ghostly appearing figure
+spectrum - n. a broad range of related objects or ideas or activities
+speculate - v. consider in an idle or casual way and with an element of doubt or without sufficient reason to reach a conclusion
+spontaneous - j. said or done without having been planned or written in advance
+static - j. showing little if any change
+stature - n. high level of respect gained by impressive development or achievement
+statute - n. an act passed by a legislative body
+stealth - n. avoiding detection by moving carefully
+stimulate - v. cause to be alert and energetic
+stringent - j. demanding strict attention to rules and procedures
+submission - n. something (manuscripts or architectural plans and models or estimates or works of art of all genres etc.) submitted for the judgment of others (as in a competition)
+subsequent - j. following in time or order
+subsidiary - j. functioning in a supporting capacity
+substantive - j. having a firm basis in reality and being therefore important, meaningful, or considerable
+subtle - j.  difficult to detect or grasp by the mind or analyze
+successor - n. a person who follows next in order
+summary - n. a brief statement that presents the main points in a concise form
+superb - j. of surpassing excellence
+superficial - j. concerned with or comprehending only what is apparent or obvious; not deep or penetrating emotionally or intellectually
+suppress - v. reduce the incidence or severity of or stop
+surround - v. extend on all sides of simultaneously; encircle
+suspense - n. excited anticipation of an approaching climax
+suspension - n. an interruption in the intensity or amount of something
+suspicious - j. openly distrustful and unwilling to confide
+sympathetic - j. expressing or feeling or resulting from sympathy or compassion or friendly fellow feelings; disposed toward
+symphony - n. a large orchestra; can perform symphonies
+systematic - j. characterized by order and planning
+tactics - n. the branch of military science dealing with detailed maneuvers to achieve objectives set by strategy
+tangible - j. perceptible by the senses especially the sense of touch
+taxation - n. the imposition of taxes; the practice of the government in levying taxes on the subjects of a state
+technique - n. skillfulness in the command of fundamentals deriving from practice and familiarity
+technology - n. the practical application of science to commerce or industry
+telescope - n. a magnifier of images of distant objects
+temporary - j. not permanent; not lasting
+tendency - n. a characteristic likelihood of or natural disposition toward a certain condition or character or effect
+tense - j. taut or rigid; stretched tight
+tentative - j. unsettled in mind or opinion
+tenure - v. give life-time employment to
+terminal - j. being or situated at an end
+territorial - j. displaying territoriality; defending an area from intruders
+testament - n. a profession of belief
+theological - j. of or relating to or concerning the study of religion
+theology - n. the rational and systematic study of religion and its influences and of the nature of religious truth
+theoretical - j. concerned with theories rather than their practical applications
+theorist - n. someone who theorizes (especially in science or art)
+thesis - n. a formal paper advancing a new point of view resulting from research; usually a requirement for an advanced academic degree
+titanic - j. of great force or power
+tolerance - n. willingness to recognize and respect the beliefs or practices of others
+tolerant - j. showing respect for the rights or opinions or practices of others
+tolerate - v. recognize and respect (rights and beliefs of others)
+transcript - n. something that has been transcribed; a written record (usually typewritten) of dictated or recorded speech
+transfer - v. move from one place to another
+transition - v. make or undergo a transition (from one state or system to another)
+translate - v. restate (words) from one language into another language
+transmission - n. the act of sending a message; causing a message to be transmitted
+transparent - j. transmitting light; able to be seen through with clarity
+transplant - n. the act of removing something from one location and introducing it in another location
+tremendous - j. extraordinarily large in size or extent or amount or power or degree
+tribune - n. a protector of the people
+trinity - n. the union of the Father and Son and Holy Ghost in one Godhead
+triple - v. increase threefold
+trivial - j. of little substance or significance
+truthful - j. expressing or given to expressing the truth
+turmoil - n. a violent disturbance
+typical - j. exhibiting the qualities or characteristics that identify a group or kind or category
+ubiquitous - j. being present everywhere at once
+ultimate - j. furthest or highest in degree or order; utmost or extreme
+unanimous - j. acting together as a single undiversified whole
+uncommon - j. not common or ordinarily encountered; unusually great in amount or remarkable in character or kind
+unconscious - j. not conscious; lacking awareness and the capacity for sensory perception as if asleep or dead
+undermine - v. destroy property or hinder normal operations
+unique - j. the single one of its kind
+unlimited - j. having no limits in range or scope
+unprecedented - j. having no previous example; novel
+urban - j. located in or characteristic of a city or city life
+urgency - n. an urgent situation calling for prompt action
+usage - n. the act of using
+utility - n. the quality of being of practical use
+vacuum - n. a region that is devoid of matter
+valid - j. well grounded in logic or truth or having legal force
+variation - n. an artifact that deviates from a norm or standard
+vegetarian - n. eater of fruits and grains and nuts; someone who eats no meat or fish or (often) any animal products
+vegetation - n. all the plant life in a particular region or period
+venerable - j. impressive by reason of age
+verify - v. confirm the truth of
+version - n. something a little different from others of the same type
+vertical - j. at right angles to the plane of the horizon or a base line
+veto - n. the power or right to prohibit or reject a proposed or intended act (especially the power of a chief executive to reject a bill passed by the legislature)
+vigorous - j. strong and active physically or mentally
+violation - n. an act that disregards an agreement or a right
+vista - n. the visual percept of a region
+visual - j. relating to or using sight
+vitality - n. an energetic style
+vogue - n. the popular taste at a given time
+volatile - j. liable to lead to sudden change or violence
+vulnerable - j. capable of being wounded or hurt
+warrant - v. stand behind and guarantee the quality, accuracy, or condition of
+wherever - a. where in the world
+wholly - a. to a complete degree or to the full or entire extent ('whole' is often used informally for 'wholly')
+woo - v. seek someone's favor
+zeal - n. excessive fervor to do something or accomplish some end
diff --git a/samples/SearchableDictionary/res/values/strings.xml b/samples/SearchableDictionary/res/values/strings.xml
new file mode 100644 (file)
index 0000000..a39ba75
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- The name of the application. -->
+    <string name="app_name">Searchable Dictionary</string>
+
+    <!-- The label for the search results for this app (see searchable.xml for more details). -->
+    <string name="search_label">Dictionary</string>
+
+    <!-- The menu entry that invokes search. -->
+    <string name="menu_search">Search</string>
+
+    <!-- The description that will show up in the search settings for this source.  -->
+    <string name="settings_description">Definitions of words</string>
+
+    <!-- General instructions in the main activity. -->
+    <string name="search_instructions">Press the search key to look up a word</string>
+
+    <!-- Shown above search results when we receive a search request. -->
+    <string name="search_results">Search results for \'<xliff:g id="string">%s</xliff:g>\': </string>
+</resources>
diff --git a/samples/SearchableDictionary/res/xml/searchable.xml b/samples/SearchableDictionary/res/xml/searchable.xml
new file mode 100644 (file)
index 0000000..1edb57c
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- The attributes below configure how the search results will work:
+ - the 'label' points to a short description used when searching within the application if
+   'badge mode' is used as specified with android:searchMode="useLabelAsBadge" (which it is not for
+    this application).
+ - the 'searchSettingsDescription' points to a string that will be displayed underneath the
+   name of this application in the search settings to describe what content will be searched.
+ - 'includeInGlobalSearch' will include this app's search suggestions in Quick Search Box.
+ - 'searchSuggestAuthority' specifies the authority matching the authority of the
+   "DictionaryProvider" specified in the manifest.  This means the DictionaryProvider will be
+   queried for search suggestions.
+ - 'searchSuggestIntentAction' the default intent action used in the intent that is launched based
+   on a user cilcking on a search suggestion.  This saves us from manually having to fill in the
+   SUGGEST_COLUMN_INTENT_ACTION column for each suggestion returned by the provider.
+ -->
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+        android:label="@string/search_label"
+        android:searchSettingsDescription="@string/settings_description"
+        android:includeInGlobalSearch="true"
+        android:searchSuggestAuthority="dictionary"
+        android:searchSuggestIntentAction="android.intent.action.VIEW">
+</searchable>
diff --git a/samples/SearchableDictionary/src/com/example/android/searchabledict/Dictionary.java b/samples/SearchableDictionary/src/com/example/android/searchabledict/Dictionary.java
new file mode 100644 (file)
index 0000000..59e735b
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.searchabledict;
+
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Contains logic to load the word of words and definitions and find a list of matching words
+ * given a query.  Everything is held in memory; this is not a robust way to serve lots of
+ * words and is only for demo purposes.
+ *
+ * You may want to consider using an SQLite database. In practice, you'll want to make sure your
+ * suggestion provider is as efficient as possible, as the system will be taxed while performing
+ * searches across many sources for each keystroke the user enters into Quick Search Box.
+ */
+public class Dictionary {
+
+    public static class Word {
+        public final String word;
+        public final String definition;
+
+        public Word(String word, String definition) {
+            this.word = word;
+            this.definition = definition;
+        }
+    }
+
+    private static final Dictionary sInstance = new Dictionary();
+
+    public static Dictionary getInstance() {
+        return sInstance;
+    }
+
+    private final Map<String, List<Word>> mDict = new ConcurrentHashMap<String, List<Word>>();
+
+    private Dictionary() {
+    }
+
+    private boolean mLoaded = false;
+
+    /**
+     * Loads the words and definitions if they haven't been loaded already.
+     *
+     * @param resources Used to load the file containing the words and definitions.
+     */
+    public synchronized void ensureLoaded(final Resources resources) {
+        if (mLoaded) return;
+
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    loadWords(resources);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }).start();
+    }
+
+    private synchronized void loadWords(Resources resources) throws IOException {
+        if (mLoaded) return;
+
+        Log.d("dict", "loading words");
+        InputStream inputStream = resources.openRawResource(R.raw.definitions);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+
+        try {
+            String line;
+            while((line = reader.readLine()) != null) {
+                String[] strings = TextUtils.split(line, "-");
+                if (strings.length < 2) continue;
+                addWord(strings[0].trim(), strings[1].trim());
+            }
+        } finally {
+            reader.close();
+        }
+        mLoaded = true;
+    }
+
+
+    public List<Word> getMatches(String query) {
+        List<Word> list = mDict.get(query);
+        return list == null ? Collections.EMPTY_LIST : list;
+    }
+
+    private void addWord(String word, String definition) {
+        final Word theWord = new Word(word, definition);
+
+        final int len = word.length();
+        for (int i = 0; i < len; i++) {
+            final String prefix = word.substring(0, len - i);
+            addMatch(prefix, theWord);
+        }
+    }
+
+    private void addMatch(String query, Word word) {
+        List<Word> matches = mDict.get(query);
+        if (matches == null) {
+            matches = new ArrayList<Word>();
+            mDict.put(query, matches);
+        }
+        matches.add(word);
+    }
+}
diff --git a/samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryProvider.java b/samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryProvider.java
new file mode 100644 (file)
index 0000000..db626e8
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.searchabledict;
+
+import android.app.SearchManager;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import java.util.List;
+
+/**
+ * Provides search suggestions for a list of words and their definitions.
+ */
+public class DictionaryProvider extends ContentProvider {
+
+    public static String AUTHORITY = "dictionary";
+
+    private static final int SEARCH_SUGGEST = 0;
+    private static final int SHORTCUT_REFRESH = 1;
+    private static final UriMatcher sURIMatcher = buildUriMatcher();
+
+    /**
+     * The columns we'll include in our search suggestions.  There are others that could be used
+     * to further customize the suggestions, see the docs in {@link SearchManager} for the details
+     * on additional columns that are supported.
+     */
+    private static final String[] COLUMNS = {
+            "_id",  // must include this column
+            SearchManager.SUGGEST_COLUMN_TEXT_1,
+            SearchManager.SUGGEST_COLUMN_TEXT_2,
+            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
+            };
+
+
+    /**
+     * Sets up a uri matcher for search suggestion and shortcut refresh queries.
+     */
+    private static UriMatcher buildUriMatcher() {
+        UriMatcher matcher =  new UriMatcher(UriMatcher.NO_MATCH);
+        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
+        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
+        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT, SHORTCUT_REFRESH);
+        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SHORTCUT_REFRESH);
+        return matcher;
+    }
+
+    @Override
+    public boolean onCreate() {
+        Resources resources = getContext().getResources();
+        Dictionary.getInstance().ensureLoaded(resources);
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        if (!TextUtils.isEmpty(selection)) {
+            throw new IllegalArgumentException("selection not allowed for " + uri);
+        }
+        if (selectionArgs != null && selectionArgs.length != 0) {
+            throw new IllegalArgumentException("selectionArgs not allowed for " + uri);
+        }
+        if (!TextUtils.isEmpty(sortOrder)) {
+            throw new IllegalArgumentException("sortOrder not allowed for " + uri);
+        }
+        switch (sURIMatcher.match(uri)) {
+            case SEARCH_SUGGEST:
+                String query = null;
+                if (uri.getPathSegments().size() > 1) {
+                    query = uri.getLastPathSegment().toLowerCase();
+                }
+                return getSuggestions(query, projection);
+            case SHORTCUT_REFRESH:
+                String shortcutId = null;
+                if (uri.getPathSegments().size() > 1) {
+                    shortcutId = uri.getLastPathSegment();
+                }
+                return refreshShortcut(shortcutId, projection);
+            default:
+                throw new IllegalArgumentException("Unknown URL " + uri);
+        }
+    }
+
+    private Cursor getSuggestions(String query, String[] projection) {
+        String processedQuery = query == null ? "" : query.toLowerCase();
+        List<Dictionary.Word> words = Dictionary.getInstance().getMatches(processedQuery);
+
+        MatrixCursor cursor = new MatrixCursor(COLUMNS);
+        for (Dictionary.Word word : words) {
+            cursor.addRow(columnValuesOfWord(word));
+        }
+
+        return cursor;
+    }
+
+    private Object[] columnValuesOfWord(Dictionary.Word word) {
+        return new String[] {
+                word.word,           // _id
+                word.word,           // text1
+                word.definition,     // text2
+                word.word,           // intent_data (included when clicking on item)
+        };
+    }
+
+    /**
+     * Note: this is unused as is, but if we included
+     * {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our results, we
+     * could expect to receive refresh queries on this uri for the id provided, in which case we
+     * would return a cursor with a single item representing the refreshed suggestion data.
+     */
+    private Cursor refreshShortcut(String shortcutId, String[] projection) {
+        return null;
+    }
+
+    /**
+     * All queries for this provider are for the search suggestion and shortcut refresh mime type.
+     */
+    public String getType(Uri uri) {
+        switch (sURIMatcher.match(uri)) {
+            case SEARCH_SUGGEST:
+                return SearchManager.SUGGEST_MIME_TYPE;
+            case SHORTCUT_REFRESH:
+                return SearchManager.SHORTCUT_MIME_TYPE;
+            default:
+                throw new IllegalArgumentException("Unknown URL " + uri);
+        }
+    }
+
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/samples/SearchableDictionary/src/com/example/android/searchabledict/SearchableDictionary.java b/samples/SearchableDictionary/src/com/example/android/searchabledict/SearchableDictionary.java
new file mode 100644 (file)
index 0000000..4d27470
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.searchabledict;
+
+import android.app.Activity;
+import android.app.SearchManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.TwoLineListItem;
+
+import java.util.List;
+
+/**
+ * The main activity for the dictionary.  Also displays search results triggered by the search
+ * dialog.
+ */
+public class SearchableDictionary extends Activity {
+
+    private static final int MENU_SEARCH = 1;
+
+    private TextView mTextView;
+    private ListView mList;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+
+        setContentView(R.layout.main);
+        mTextView = (TextView) findViewById(R.id.textField);
+        mList = (ListView) findViewById(R.id.list);
+
+        if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+            // from click on search results
+            Dictionary.getInstance().ensureLoaded(getResources());
+            String word = intent.getDataString();
+            Dictionary.Word theWord = Dictionary.getInstance().getMatches(word).get(0);
+            launchWord(theWord);
+            finish();
+        } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+            String query = intent.getStringExtra(SearchManager.QUERY);
+            mTextView.setText(getString(R.string.search_results, query));
+            WordAdapter wordAdapter = new WordAdapter(Dictionary.getInstance().getMatches(query));
+            mList.setAdapter(wordAdapter);
+            mList.setOnItemClickListener(wordAdapter);
+        }
+
+        Log.d("dict", intent.toString());
+        if (intent.getExtras() != null) {
+            Log.d("dict", intent.getExtras().keySet().toString());
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
+                .setIcon(android.R.drawable.ic_search_category_default)
+                .setAlphabeticShortcut(SearchManager.MENU_KEY);
+
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_SEARCH:
+                onSearchRequested();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void launchWord(Dictionary.Word theWord) {
+        Intent next = new Intent();
+        next.setClass(this, WordActivity.class);
+        next.putExtra("word", theWord.word);
+        next.putExtra("definition", theWord.definition);
+        startActivity(next);
+    }
+
+    class WordAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
+
+        private final List<Dictionary.Word> mWords;
+        private final LayoutInflater mInflater;
+
+        public WordAdapter(List<Dictionary.Word> words) {
+            mWords = words;
+            mInflater = (LayoutInflater) SearchableDictionary.this.getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+        }
+
+        public int getCount() {
+            return mWords.size();
+        }
+
+        public Object getItem(int position) {
+            return position;
+        }
+
+        public long getItemId(int position) {
+            return position;
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TwoLineListItem view = (convertView != null) ? (TwoLineListItem) convertView :
+                    createView(parent);
+            bindView(view, mWords.get(position));
+            return view;
+        }
+
+        private TwoLineListItem createView(ViewGroup parent) {
+            TwoLineListItem item = (TwoLineListItem) mInflater.inflate(
+                    android.R.layout.simple_list_item_2, parent, false);
+            item.getText2().setSingleLine();
+            item.getText2().setEllipsize(TextUtils.TruncateAt.END);
+            return item;
+        }
+
+        private void bindView(TwoLineListItem view, Dictionary.Word word) {
+            view.getText1().setText(word.word);
+            view.getText2().setText(word.definition);
+        }
+
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            launchWord(mWords.get(position));
+        }
+    }
+}
diff --git a/samples/SearchableDictionary/src/com/example/android/searchabledict/WordActivity.java b/samples/SearchableDictionary/src/com/example/android/searchabledict/WordActivity.java
new file mode 100644 (file)
index 0000000..1c4b8b4
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.searchabledict;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.content.Intent;
+
+/**
+ * Displays a word and its definition.
+ */
+public class WordActivity extends Activity {
+
+    private TextView mWord;
+    private TextView mDefinition;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.word);
+
+        mWord = (TextView) findViewById(R.id.word);
+        mDefinition = (TextView) findViewById(R.id.definition);
+
+        Intent intent = getIntent();
+
+        String word = intent.getStringExtra("word");
+        String definition = intent.getStringExtra("definition");
+
+        mWord.setText(word);
+        mDefinition.setText(definition);
+    }
+}
diff --git a/samples/Snake/_index.html b/samples/Snake/_index.html
new file mode 100644 (file)
index 0000000..991bc5d
--- /dev/null
@@ -0,0 +1,10 @@
+<p>This is an implementation of the classic Game "Snake", in which you control a
+serpent roaming around the garden looking for apples. Be careful, though,
+because when you catch one, not only will you become longer, but you'll move
+faster. Running into yourself or the walls will end the game..</p>
+
+<p>This code demonstrates how to create custom View layouts and
+request draws to the screen. A great example of a basic game that does not
+require a fast framerate.</p>
+
+<img alt="" src="../images/Snake.png" />
diff --git a/samples/SoftKeyboard/_index.html b/samples/SoftKeyboard/_index.html
new file mode 100644 (file)
index 0000000..49f197d
--- /dev/null
@@ -0,0 +1,7 @@
+<p>This application is an example of writing an input method for a software keyboard.  
+This code is focused on simplicity over completeness, so it should in no way be considered
+to be a complete soft keyboard implementation.  Its purpose is to provide
+a basic example for how you would get started writing an input method, to
+be fleshed out as appropriate.</p>
+
+<img alt="" src="../images/SoftKeyboard.png" />
\ No newline at end of file
index 8548823..6f073bb 100644 (file)
@@ -72,7 +72,7 @@ See test_defs.xsd for more information.
 <test name="launchperf"
     build_path="development/apps/launchperf"
     package="com.android.launchperf"
-    class="com.android.launchperf.SimpleActivityLaunchPerformance"
+    runner=".SimpleActivityLaunchPerformance"
     coverage_target="framework" />
 
 <!--  targeted framework tests -->
@@ -183,6 +183,13 @@ See test_defs.xsd for more information.
     runner="android.test.InstrumentationCtsTestRunner"
     coverage_target="framework"
     cts="true" />
+    
+<test name="cts-gesture"
+    build_path="cts/tests/tests/gesture"
+    package="com.android.cts.gesture"
+    runner="android.test.InstrumentationTestRunner"
+    coverage_target="framework"
+    cts="true" />
 
 <test name="cts-graphics"
     build_path="cts/tests"
@@ -263,6 +270,13 @@ See test_defs.xsd for more information.
     coverage_target="framework"
     cts="true" />
 
+<test name="cts-telephony"
+    build_path="cts/tests"
+    package="com.android.cts.telephony"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
 <test name="cts-util"
     build_path="cts/tests"
     package="com.android.cts.util"
@@ -305,11 +319,11 @@ See test_defs.xsd for more information.
     coverage_target="Calendar"
     continuous="true" />
 
-<!-- Make continuous = "true" once the bug 1966269 is fixed -->
 <test name="calprov"
     build_path="packages/providers/CalendarProvider/tests"
     package="com.android.providers.calendar.tests"
-    coverage_target="CalendarProvider" />
+    coverage_target="CalendarProvider"
+    continuous="true" />
 
 <test name="camerastress"
     build_path="packages/apps/Camera"
@@ -459,6 +473,12 @@ See test_defs.xsd for more information.
     description="Android STL."
     extra_build_args="ASTL_TESTS=1" />
 
+<!-- Android Keystore tests -->
+<test-native name="netkeystore_test"
+    build_path="frameworks/base/cmds/keystore/tests"
+    description="Android keystore."
+    extra_build_args="KEYSTORE_TESTS=1" />
+
 <!-- pending patch 820
 <test-native name="gtest"
     build_path="external/gtest"
index 1541cc1..d250de2 100644 (file)
@@ -125,7 +125,7 @@ class NativeTestSuite(AbstractTestSuite):
     """
     for f in files:
       (name, ext) = os.path.splitext(f)
-      if ext == ".cc" or ext == ".cpp":
+      if ext == ".cc" or ext == ".cpp" or ext == ".c":
         if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_", name):
           logger.SilentLog("Found %s" % f)
           test_list.append(str(os.path.join(dirname, f)))
index cfd9f53..446c426 100644 (file)
@@ -37,34 +37,44 @@ public final class AndroidLocation {
             super(string);
         }
     }
-    
+
     private static String sPrefsLocation = null;
-    
+
     /**
      * Returns the folder used to store android related files.
      * @return an OS specific path, terminated by a separator.
-     * @throws AndroidLocationException 
+     * @throws AndroidLocationException
      */
     public final static String getFolder() throws AndroidLocationException {
         if (sPrefsLocation == null) {
             String home = findValidPath("ANDROID_SDK_HOME", "user.home", "HOME");
-            
+
             // if the above failed, we throw an exception.
             if (home == null) {
                 throw new AndroidLocationException(
                         "Unable to get the home directory. Make sure the user.home property is set up");
             } else {
                 sPrefsLocation = home + File.separator + ".android" + File.separator;
+            }
+        }
 
-                // make sure the folder exists!
-                File f = new File(sPrefsLocation);
-                if (f.exists() == false) {
-                    f.mkdir();
-                } else if (f.isFile()) {
-                    throw new AndroidLocationException(sPrefsLocation +
-                            " is not a directory! This is required to run Android tools.");
-                }
+        // make sure the folder exists!
+        File f = new File(sPrefsLocation);
+        if (f.exists() == false) {
+            try {
+                f.mkdir();
+            } catch (SecurityException e) {
+                AndroidLocationException e2 = new AndroidLocationException(String.format(
+                        "Unable to create folder '%1$s'. " +
+                        "This is the path of preference folder expected by the Android tools.",
+                        sPrefsLocation));
+                e2.initCause(e);
+                throw e2;
             }
+        } else if (f.isFile()) {
+            throw new AndroidLocationException(sPrefsLocation +
+                    " is not a directory! " +
+                    "This is the path of preference folder expected by the Android tools.");
         }
 
         return sPrefsLocation;
@@ -92,7 +102,7 @@ public final class AndroidLocation {
                 }
             }
         }
-        
+
         return null;
     }
 }
index af87c78..9231dc9 100644 (file)
@@ -28,6 +28,7 @@ import org.apache.tools.ant.Project;
 import org.apache.tools.ant.ProjectComponent;
 import org.apache.tools.ant.Task;
 import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Path.PathElement;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -39,6 +40,9 @@ import java.util.Map.Entry;
 
 public class ApkBuilderTask extends Task {
 
+    // ref id to the <path> object containing all the boot classpaths.
+    private final static String REF_APK_PATH = "android.apks.path";
+
     /**
      * Class to represent nested elements. Since they all have only one attribute ('path'), the
      * same class can be used for all the nested elements (zip, file, sourcefolder, jarfolder,
@@ -152,7 +156,7 @@ public class ApkBuilderTask extends Task {
 
     @Override
     public void execute() throws BuildException {
-        Project taskProject = getProject();
+        Project antProject = getProject();
 
         ApkBuilderImpl apkBuilder = new ApkBuilderImpl();
         apkBuilder.setVerbose(mVerbose);
@@ -197,13 +201,34 @@ public class ApkBuilderTask extends Task {
                 ApkBuilderImpl.processNativeFolder(offset, f, mNativeLibraries);
             }
 
+            // create the Path item that will contain all the generated APKs
+            // for reuse by other targets (signing/zipaligning)
+            Path path = new Path(antProject);
+
+            // The createApk method uses mBaseName for the base name of the packages (resources
+            // and apk files).
+            // The generated apk file name is
+            // debug:   {base}[-{config}]-debug-unaligned.apk
+            // release: {base}[-{config}]-unsigned.apk
+            // Unfortunately for 1.5 projects and before the 'install' ant target expects the name
+            // of the default debug package to be {base}-debug.apk
+            // In order to support those package, we look for the 'out-debug-unaligned-package'
+            // property. If this exist, then we generate {base}[-{config}]-debug-unaligned.apk
+            // otherwise we generate {base}[-{config}]-debug.apk
+            // FIXME: Make apkbuilder export the package name used instead of
+            // having to keep apkbuilder and the rules file in sync
+            String debugPackageSuffix = "-debug-unaligned.apk";
+            if (antProject.getProperty("out-debug-unaligned-package") == null) {
+                debugPackageSuffix = "-debug.apk";
+            }
 
             // first do a full resource package
-            createApk(apkBuilder, null /*configName*/, null /*resourceFilter*/);
+            createApk(apkBuilder, null /*configName*/, null /*resourceFilter*/, path,
+                    debugPackageSuffix);
 
             // now see if we need to create file with filtered resources.
             // Get the project base directory.
-            File baseDir = taskProject.getBaseDir();
+            File baseDir = antProject.getBaseDir();
             ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(),
                     PropertyType.DEFAULT);
 
@@ -211,9 +236,14 @@ public class ApkBuilderTask extends Task {
             if (apkConfigs.size() > 0) {
                 Set<Entry<String, String>> entrySet = apkConfigs.entrySet();
                 for (Entry<String, String> entry : entrySet) {
-                    createApk(apkBuilder, entry.getKey(), entry.getValue());
+                    createApk(apkBuilder, entry.getKey(), entry.getValue(), path,
+                            debugPackageSuffix);
                 }
             }
+
+            // finally sets the path in the project with a reference
+            antProject.addReference(REF_APK_PATH, path);
+
         } catch (FileNotFoundException e) {
             throw new BuildException(e);
         } catch (IllegalArgumentException e) {
@@ -230,10 +260,13 @@ public class ApkBuilderTask extends Task {
      * package will be generated.
      * @param resourceFilter the resource configuration filter to pass to aapt (if configName is
      * non null)
+     * @param path Ant {@link Path} to which add the generated APKs as {@link PathElement}
+     * @param debugPackageSuffix suffix for the debug packages.
      * @throws FileNotFoundException
      * @throws ApkCreationException
      */
-    private void createApk(ApkBuilderImpl apkBuilder, String configName, String resourceFilter)
+    private void createApk(ApkBuilderImpl apkBuilder, String configName, String resourceFilter,
+            Path path, String debugPackageSuffix)
             throws FileNotFoundException, ApkCreationException {
         // All the files to be included in the archive have already been prep'ed up, except
         // the resource package.
@@ -259,7 +292,7 @@ public class ApkBuilderTask extends Task {
         }
 
         if (mSigned) {
-            filename = filename + "-debug.apk";
+            filename = filename + debugPackageSuffix;
         } else {
             filename = filename + "-unsigned.apk";
         }
@@ -284,8 +317,13 @@ public class ApkBuilderTask extends Task {
             }
         }
 
+        // out File
         File f = new File(mOutFolder, filename);
 
+        // add it to the Path object
+        PathElement element = path.createPathElement();
+        element.setLocation(f);
+
         // and generate the apk
         apkBuilder.createPackage(f.getAbsoluteFile(), mZipArchives,
                 mArchiveFiles, mJavaResources, mResourcesJars, mNativeLibraries);
index e739044..6dd3ee7 100644 (file)
@@ -2,7 +2,7 @@
 <feature
       id="com.android.ide.eclipse.adt"
       label="Android Development Tools"
-      version="0.9.2.qualifier"
+      version="0.9.3.qualifier"
       provider-name="The Android Open Source Project"
       plugin="com.android.ide.eclipse.adt">
 
    </copyright>
 
    <license url="http://www.eclipse.org/org/documents/epl-v10.php">
-      Note:  jcommon-1.0.12.jar is under the BSD license rather than the APL.  You can find a copy of the BSD License at http://www.opensource.org/licenses/bsd-license.php
+      Note:  kxml2-2.3.0.jar is under the BSD license rather than the EPL.  You can find a copy of the BSD License at http://www.opensource.org/licenses/bsd-license.php
 
-jfreechart-1.0.9.jar and jfreechart-1.0.9-swt.jar are under the LGPL rather than the EPL.  You can find a copy of the LGPL at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt.  You can get the source code for these two components at http://android.git.kernel.org/pub/jfreechart-1.0.9.zip
-
-   
       Eclipse Public License - v 1.0
 
 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT&apos;S ACCEPTANCE OF THIS AGREEMENT.
index e70c1b9..e67f960 100644 (file)
@@ -2,7 +2,7 @@
 <feature
       id="com.android.ide.eclipse.ddms"
       label="Android DDMS"
-      version="0.9.2.qualifier"
+      version="0.9.3.qualifier"
       provider-name="The Android Open Source Project">
 
    <description>
@@ -16,9 +16,9 @@
    <license url="http://www.apache.org/licenses/LICENSE-2.0">
       Note:  jcommon-1.0.12.jar is under the BSD license rather than the APL.  You can find a copy of the BSD License at http://www.opensource.org/licenses/bsd-license.php
 
-   jfreechart-1.0.9.jar and jfreechart-1.0.9-swt.jar are under the LGPL rather than the EPL.  You can find a copy of the LGPL at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt.  You can get the source code for these two components at http://android.git.kernel.org/pub/jfreechart-1.0.9.zip
-   
-   
+   jfreechart-1.0.9.jar and jfreechart-1.0.9-swt.jar are under the LGPL rather than the APL.  You can find a copy of the LGPL at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt.  You can get the source code for these two components at http://android.git.kernel.org/pub/jfreechart-1.0.9.zip
+
+
       Apache License
                            Version 2.0, January 2004
                         http://www.apache.org/licenses/
index d2778fe..cc34045 100644 (file)
@@ -2,7 +2,7 @@
 <feature
       id="com.android.ide.eclipse.tests"
       label="ADT Tests"
-      version="0.9.2.qualifier"
+      version="0.9.3.qualifier"
       provider-name="The Android Open Source Project">
 
    <copyright>
index 7474b70..5684195 100644 (file)
@@ -3,8 +3,8 @@
        <classpathentry excluding="Makefile|resources/" kind="src" path="src"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="lib" path="jarutils.jar"/>
-       <classpathentry kind="lib" path="androidprefs.jar"/>
+       <classpathentry kind="lib" path="jarutils.jar" sourcepath="/JarUtils"/>
+       <classpathentry kind="lib" path="androidprefs.jar" sourcepath="/AndroidPrefs"/>
        <classpathentry kind="lib" path="sdkstats.jar" sourcepath="/SdkStatsService"/>
        <classpathentry kind="lib" path="kxml2-2.3.0.jar"/>
        <classpathentry kind="lib" path="layoutlib_api.jar"/>
index f9c1e9c..32c939b 100644 (file)
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: Android Development Toolkit
 Bundle-SymbolicName: com.android.ide.eclipse.adt;singleton:=true
-Bundle-Version: 0.9.2.qualifier
+Bundle-Version: 0.9.3.qualifier
 Bundle-ClassPath: .,
  jarutils.jar,
  androidprefs.jar,
index 76eefe6..f045b06 100644 (file)
          </menu>
       </actionSet>
       <actionSet
-            description="Android AVD Manager"
+            description="Android AVD and SDK Manager"
             id="adt.actionSet.avdManager"
-            label="Android AVD Manager"
+            label="Android SDK and AVD Manager"
             visible="true">
          <action
                class="com.android.ide.eclipse.adt.internal.wizards.actions.AvdManagerAction"
                icon="icons/avd_manager.png"
                id="com.android.ide.eclipse.adt.ui.avdmanager"
-               label="Android AVD Manager"
+               label="Android SDK and AVD Manager"
                menubarPath="Window/additions"
                style="push"
                toolbarPath="android_project"
-               tooltip="Opens the Android Virtual Device (AVD) Manager">
+               tooltip="Opens the Android Virtual Device (AVD) and SDK Manager">
          </action>
       </actionSet>
    </extension>
index d213f7a..4aa3a80 100644 (file)
@@ -454,12 +454,17 @@ public class AdtPlugin extends AbstractUIPlugin {
 
     /** Returns the adb path relative to the sdk folder */
     public static String getOsRelativeAdb() {
-        return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB;
+        return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ADB;
+    }
+
+    /** Returns the zipalign path relative to the sdk folder */
+    public static String getOsRelativeZipAlign() {
+        return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ZIPALIGN;
     }
 
     /** Returns the emulator path relative to the sdk folder */
     public static String getOsRelativeEmulator() {
-        return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR;
+        return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_EMULATOR;
     }
 
     /** Returns the absolute adb path */
@@ -467,6 +472,11 @@ public class AdtPlugin extends AbstractUIPlugin {
         return getOsSdkFolder() + getOsRelativeAdb();
     }
 
+    /** Returns the absolute zipalign path */
+    public static String getOsAbsoluteZipAlign() {
+        return getOsSdkFolder() + getOsRelativeZipAlign();
+    }
+
     /** Returns the absolute traceview path */
     public static String getOsAbsoluteTraceview() {
         return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER +
index 4464daa..a09ee3c 100644 (file)
@@ -106,10 +106,6 @@ public class AndroidConstants {
     public final static Pattern PATTERN_RESOURCES_S_AP_ =
         Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
 
-    public final static String FN_ADB = SdkConstants.FN_ADB;
-
-    public final static String FN_EMULATOR = SdkConstants.FN_EMULATOR;
-
     public final static String FN_TRACEVIEW =
         (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
             "traceview.exe" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$
index 2ba0b03..eb8fc53 100644 (file)
@@ -291,6 +291,9 @@ public class PreCompilerBuilder extends BaseBuilder {
 
                 // This interrupts the build. The next builders will not run.
                 stopBuild(msg);
+
+                // TODO: document whether code below that uses manifest (which is now guaranteed
+                // to be null) will actually be executed or not.
             }
 
             // lets check the XML of the manifest first, if that hasn't been done by the
@@ -332,9 +335,8 @@ public class PreCompilerBuilder extends BaseBuilder {
                     if (codename != null) {
                         // integer minSdk when the target is a preview => fatal error
                         String msg = String.format(
-                                "Platform %1$s is a preview and requires appication manifests to set %2$s to '%3$s'",
-                                codename, ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION,
-                                codename);
+                                "Platform %1$s is a preview and requires appication manifests to set %2$s to '%1$s'",
+                                codename, ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION);
                         AdtPlugin.printErrorToConsole(project, msg);
                         BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
                                 IMarker.SEVERITY_ERROR);
@@ -342,7 +344,7 @@ public class PreCompilerBuilder extends BaseBuilder {
                     } else if (minSdkValue < projectVersion.getApiLevel()) {
                         // integer minSdk is not high enough for the target => warning
                         String msg = String.format(
-                                "Manifest min SDK version (%1$d) is lower than project target API level (%2$d)",
+                                "Manifest min SDK version (%1$s) is lower than project target API level (%2$d)",
                                 minSdkVersion, projectVersion.getApiLevel());
                         AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
                         BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
@@ -356,7 +358,7 @@ public class PreCompilerBuilder extends BaseBuilder {
                         // platform is not a preview => fatal error
                         String msg = String.format(
                                 "Manifest attribute '%1$s' is set to '%2$s'. Integer is expected.",
-                                ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION, codename);
+                                ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION, minSdkVersion);
                         AdtPlugin.printErrorToConsole(project, msg);
                         BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
                                 IMarker.SEVERITY_ERROR);
@@ -372,6 +374,17 @@ public class PreCompilerBuilder extends BaseBuilder {
                         stopBuild(msg);
                     }
                 }
+            } else if (projectTarget.getVersion().isPreview()) {
+                // else the minSdkVersion is not set but we are using a preview target.
+                // Display an error
+                String codename = projectTarget.getVersion().getCodename();
+                String msg = String.format(
+                        "Platform %1$s is a preview and requires appication manifests to set %2$s to '%1$s'",
+                        codename, ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION);
+                AdtPlugin.printErrorToConsole(project, msg);
+                BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
+                        IMarker.SEVERITY_ERROR);
+                stopBuild(msg);
             }
 
             if (javaPackage == null || javaPackage.length() == 0) {
@@ -383,11 +396,14 @@ public class PreCompilerBuilder extends BaseBuilder {
 
                 // This interrupts the build. The next builders will not run.
                 stopBuild(msg);
+
+                // TODO: document whether code below that uses javaPackage (which is now guaranteed
+                // to be null) will actually be executed or not.
             }
 
             // at this point we have the java package. We need to make sure it's not a different
             // package than the previous one that were built.
-            if (javaPackage.equals(mManifestPackage) == false) {
+            if (javaPackage != null && javaPackage.equals(mManifestPackage) == false) {
                 // The manifest package has changed, the user may want to update
                 // the launch configuration
                 if (mManifestPackage != null) {
@@ -421,7 +437,7 @@ public class PreCompilerBuilder extends BaseBuilder {
                 // get the file system path
                 IPath outputLocation = mGenFolder.getLocation();
                 IPath resLocation = resFolder.getLocation();
-                IPath manifestLocation = manifest.getLocation();
+                IPath manifestLocation = manifest == null ? null : manifest.getLocation();
 
                 // those locations have to exist for us to do something!
                 if (outputLocation != null && resLocation != null
index c940b9f..225d5bd 100644 (file)
@@ -4,7 +4,7 @@
  * 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
@@ -43,6 +43,7 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMe
 import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier;
 import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier.KeyboardState;
 import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier.NavigationMethod;
+import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density;
 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
 import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier.TextInputMethod;
 import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier.TouchScreenType;
@@ -55,7 +56,6 @@ import com.android.ide.eclipse.adt.internal.sdk.LoadStatus;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
-import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.DensityVerifier;
 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.DimensionVerifier;
 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.LanguageRegionVerifier;
 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.MobileCodeVerifier;
@@ -143,7 +143,7 @@ import java.util.Set;
  */
 public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         implements ILayoutReloadListener {
-    
+
     private final static String THEME_SEPARATOR = "----------"; //$NON-NLS-1$
 
     /** Reference to the layout editor */
@@ -161,7 +161,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
     private Combo mLanguage;
     private Combo mRegion;
     private Combo mOrientation;
-    private Text mDensity;
+    private Combo mDensity;
     private Combo mTouch;
     private Combo mKeyboard;
     private Combo mTextInput;
@@ -216,7 +216,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         public void onTargetsLoaded() {
             // because the SDK changed we must reset the configured framework resource.
             mConfiguredFrameworkRes = null;
-            
+
             updateUIFromResources();
 
             mThemeCombo.getParent().layout();
@@ -383,19 +383,19 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
 
         new Label(topParent, SWT.NONE).setText("Density");
         mDensityIcon = createControlComposite(topParent, true /* grab_horizontal */);
-        mDensity = new Text(mDensityIcon.getParent(), SWT.BORDER);
+        mDensity = new Combo(mDensityIcon.getParent(), SWT.DROP_DOWN | SWT.READ_ONLY);
+        Density[] dValues = Density.values();
+        mDensity.add("(Default)");
+        for (Density value : dValues) {
+            mDensity.add(value.getDisplayValue());
+        }
+        mDensity.select(0);
         mDensity.setLayoutData(new GridData(
                 GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
-        mDensity.addVerifyListener(new DensityVerifier());
         mDensity.addSelectionListener(new SelectionAdapter() {
-            @Override
-            public void widgetDefaultSelected(SelectionEvent e) {
-                onDensityChange();
-            }
-        });
-        mDensity.addModifyListener(new ModifyListener() {
-            public void modifyText(ModifyEvent e) {
-                onDensityChange();
+           @Override
+            public void widgetSelected(SelectionEvent e) {
+               onDensityChange();
             }
         });
 
@@ -468,7 +468,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             @Override
              public void widgetSelected(SelectionEvent e) {
                 onNavigationChange();
-            } 
+            }
         });
 
         Composite labelParent = new Composite(topParent, SWT.NONE);
@@ -512,7 +512,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
 
         mSize1.addSelectionListener(sl);
         mSize2.addSelectionListener(sl);
-        
+
         ModifyListener sizeModifyListener = new ModifyListener() {
             public void modifyText(ModifyEvent e) {
                 onSizeChange();
@@ -551,11 +551,12 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             @Override
             public void widgetSelected(SelectionEvent e) {
                 LayoutCreatorDialog dialog = new LayoutCreatorDialog(mCreateButton.getShell(),
-                        mEditedFile.getName(), mCurrentConfig);
+                        mEditedFile.getName(),
+                        Sdk.getCurrent().getTarget(mEditedFile.getProject()), mCurrentConfig);
                 if (dialog.open() == Dialog.OK) {
                     final FolderConfiguration config = new FolderConfiguration();
                     dialog.getConfiguration(config);
-                    
+
                     createAlternateLayout(config);
                 }
             }
@@ -591,7 +592,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
     @Override
     protected PaletteRoot getPaletteRoot() {
         mPaletteRoot = PaletteFactory.createPaletteRoot(mPaletteRoot,
-                mLayoutEditor.getTargetData()); 
+                mLayoutEditor.getTargetData());
         return mPaletteRoot;
     }
 
@@ -618,7 +619,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         getCommandStack().markSaveLocation();
         firePropertyChange(PROP_DIRTY);
     }
-    
+
     @Override
     protected void configurePaletteViewer() {
         super.configurePaletteViewer();
@@ -628,7 +629,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         // the PaletteTemplateEntry held in the PaletteRoot.
         TemplateTransferDragSourceListener dragSource =
             new TemplateTransferDragSourceListener(getPaletteViewer());
-        
+
         // Create a drag source on the palette viewer.
         // See the drag target associated with the GraphicalViewer in configureGraphicalViewer.
         getPaletteViewer().addDragSourceListener(dragSource);
@@ -645,12 +646,12 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         viewer.setEditPartFactory(new UiElementsEditPartFactory(mParent.getDisplay()));
         viewer.setRootEditPart(new ScalableFreeformRootEditPart());
 
-        // Disable the following -- we don't drag *from* the GraphicalViewer yet: 
+        // Disable the following -- we don't drag *from* the GraphicalViewer yet:
         // viewer.addDragSourceListener(new TemplateTransferDragSourceListener(viewer));
-        
+
         viewer.addDropTargetListener(new DropListener(viewer));
     }
-    
+
     class DropListener extends TemplateTransferDropTargetListener {
         public DropListener(EditPartViewer viewer) {
             super(viewer);
@@ -670,7 +671,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                 public Object getObjectType() {
                     return template;
                 }
-                
+
             };
         }
     }
@@ -698,7 +699,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                     input.toString());
         }
     }
-    
+
     /* (non-javadoc)
      * Sets the graphicalViewer for this EditorPart.
      * @param viewer the graphical viewer
@@ -714,18 +715,18 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
     /**
      * Used by LayoutEditor.UiEditorActions.selectUiNode to select a new UI Node
      * created by  {@link ElementCreateCommand#execute()}.
-     * 
+     *
      * @param uiNodeModel The {@link UiElementNode} to select.
      */
     @Override
     void selectModel(UiElementNode uiNodeModel) {
         GraphicalViewer viewer = getGraphicalViewer();
-        
+
         // Give focus to the graphical viewer (in case the outline has it)
         viewer.getControl().forceFocus();
-        
+
         Object editPart = viewer.getEditPartRegistry().get(uiNodeModel);
-        
+
         if (editPart instanceof EditPart) {
             viewer.select((EditPart)editPart);
         }
@@ -745,7 +746,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         MenuManager menuManager = new MenuManager();
         menuManager.setRemoveAllWhenShown(true);
         menuManager.addMenuListener(new ActionMenuListener(viewer));
-        
+
         return menuManager;
     }
 
@@ -775,13 +776,13 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                    }
                }
            }
-           
+
            if (selected.size() > 0) {
                doCreateMenuAction(manager, mViewer, selected);
            }
         }
     }
-    
+
     private void doCreateMenuAction(IMenuManager manager,
             final GraphicalViewer viewer,
             final ArrayList<UiElementNode> selected) {
@@ -820,7 +821,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         // Append "add" and "remove" actions. They do the same thing as the add/remove
         // buttons on the side.
         IconFactory factory = IconFactory.getInstance();
-        
+
         final UiEditorActions uiActions = mLayoutEditor.getUiEditorActions();
 
         // "Add" makes sense only if there's 0 or 1 item selected since the
@@ -845,7 +846,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             });
 
             manager.add(new Separator());
-            
+
             manager.add(new Action("Up", factory.getImageDescriptor("up")) { //$NON-NLS-2$
                 @Override
                 public void run() {
@@ -859,8 +860,8 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                 }
             });
         }
-        
-    } 
+
+    }
 
     /**
      * Sets the UI for the edition of a new file.
@@ -870,11 +871,11 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
     void editNewFile(FolderConfiguration configuration) {
         // update the configuration UI
         setConfiguration(configuration, true /*force*/);
-        
+
         // enable the create button if the current and edited config are not equals
         mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false);
     }
-    
+
     public Rectangle getBounds() {
         ScreenOrientation orientation = null;
         if (mOrientation.getSelectionIndex() == 0) {
@@ -916,7 +917,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                 return new Rectangle(0, 0, s1, s1);
         }
     }
-    
+
     /**
      * Renders an Android View described by a {@link ViewElementDescriptor}.
      * <p/>This uses the <code>wrap_content</code> mode for both <code>layout_width</code> and
@@ -928,17 +929,17 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         if (mEditedFile == null) {
             return null;
         }
-        
+
         IAndroidTarget target = Sdk.getCurrent().getTarget(mEditedFile.getProject());
         if (target == null) {
             return null;
         }
-        
+
         AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
         if (data == null) {
             return null;
         }
-        
+
         LayoutBridge bridge = data.getLayoutBridge();
 
         if (bridge.bridge != null) { // bridge can never be null.
@@ -961,7 +962,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                     // get the project resource values based on the current config
                     mConfiguredProjectRes = projectRes.getConfiguredResources(mCurrentConfig);
                 }
-                
+
                 configuredProjectResources = mConfiguredProjectRes;
             } else {
                 // we absolutely need a Map of configured project resources.
@@ -998,12 +999,12 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                         int height = result.getRootView().getBottom();
                         Raster raster = largeImage.getData(new java.awt.Rectangle(width, height));
                         int[] imageDataBuffer = ((DataBufferInt)raster.getDataBuffer()).getData();
-                        
+
                         ImageData imageData = new ImageData(width, height, 32,
                                 new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF));
 
                         imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
-                        
+
                         return imageData;
                     }
                 }
@@ -1046,7 +1047,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             mNeedsXmlReload = true;
         }
     }
-    
+
     /**
      * Actually performs the XML reload
      * @see #onXmlModelChanged()
@@ -1054,17 +1055,17 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
     private void doXmlReload(boolean force) {
         if (force || mNeedsXmlReload) {
             GraphicalViewer viewer = getGraphicalViewer();
-            
+
             // try to preserve the selection before changing the content
             SelectionManager selMan = viewer.getSelectionManager();
             ISelection selection = selMan.getSelection();
-    
+
             try {
                 viewer.setContents(getModel());
             } finally {
                 selMan.setSelection(selection);
             }
-            
+
             mNeedsXmlReload = false;
         }
     }
@@ -1148,12 +1149,13 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         mDensityIcon.setImage(mMatchImage);
         PixelDensityQualifier densityQualifier = config.getPixelDensityQualifier();
         if (densityQualifier != null) {
-            mDensity.setText(String.format("%1$d", densityQualifier.getValue()));
+            mDensity.select(
+                    Density.getIndex(densityQualifier.getValue()) + 1);
             mCurrentConfig.setPixelDensityQualifier(densityQualifier);
         } else if (force) {
-            mDensity.setText(""); //$NON-NLS-1$
+            mOrientation.select(0);
             mCurrentConfig.setPixelDensityQualifier(null);
-        } else if (mDensity.getText().length() > 0) {
+        } else if (mDensity.getSelectionIndex() != 0) {
             mDensityIcon.setImage(mWarningImage);
         }
 
@@ -1223,10 +1225,10 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         // update the string showing the folder name
         String current = config.toDisplayString();
         mCurrentLayoutLabel.setText(current != null ? current : "(Default)");
-        
+
         mDisableUpdates = false;
     }
-    
+
     /**
      * Displays an error icon in front of all the non-null qualifiers.
      */
@@ -1236,44 +1238,44 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         if (countryQualifier != null) {
             mCountryIcon.setImage(mErrorImage);
         }
-        
+
         mNetworkIcon.setImage(mMatchImage);
         NetworkCodeQualifier networkQualifier = mCurrentConfig.getNetworkCodeQualifier();
         if (networkQualifier != null) {
             mNetworkIcon.setImage(mErrorImage);
         }
-        
+
         mLanguageIcon.setImage(mMatchImage);
         LanguageQualifier languageQualifier = mCurrentConfig.getLanguageQualifier();
         if (languageQualifier != null) {
             mLanguageIcon.setImage(mErrorImage);
         }
-        
+
         mRegionIcon.setImage(mMatchImage);
         RegionQualifier regionQualifier = mCurrentConfig.getRegionQualifier();
         if (regionQualifier != null) {
             mRegionIcon.setImage(mErrorImage);
         }
-        
+
         mOrientationIcon.setImage(mMatchImage);
         ScreenOrientationQualifier orientationQualifier =
             mCurrentConfig.getScreenOrientationQualifier();
         if (orientationQualifier != null) {
             mOrientationIcon.setImage(mErrorImage);
         }
-        
+
         mDensityIcon.setImage(mMatchImage);
         PixelDensityQualifier densityQualifier = mCurrentConfig.getPixelDensityQualifier();
         if (densityQualifier != null) {
             mDensityIcon.setImage(mErrorImage);
         }
-        
+
         mTouchIcon.setImage(mMatchImage);
         TouchScreenQualifier touchQualifier = mCurrentConfig.getTouchTypeQualifier();
         if (touchQualifier != null) {
             mTouchIcon.setImage(mErrorImage);
         }
-        
+
         mKeyboardIcon.setImage(mMatchImage);
         KeyboardStateQualifier keyboardQualifier = mCurrentConfig.getKeyboardStateQualifier();
         if (keyboardQualifier != null) {
@@ -1285,20 +1287,20 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         if (inputQualifier != null) {
             mTextInputIcon.setImage(mErrorImage);
         }
-        
+
         mNavigationIcon.setImage(mMatchImage);
         NavigationMethodQualifier navigationQualifiter =
             mCurrentConfig.getNavigationMethodQualifier();
         if (navigationQualifiter != null) {
             mNavigationIcon.setImage(mErrorImage);
         }
-        
+
         mSizeIcon.setImage(mMatchImage);
         ScreenDimensionQualifier sizeQualifier = mCurrentConfig.getScreenDimensionQualifier();
         if (sizeQualifier != null) {
             mSizeIcon.setImage(mErrorImage);
         }
-        
+
         // update the string showing the folder name
         String current = mCurrentConfig.toDisplayString();
         mCurrentLayoutLabel.setText(current != null ? current : "(Default)");
@@ -1308,7 +1310,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
     UiDocumentNode getModel() {
         return mLayoutEditor.getUiRootNode();
     }
-    
+
     @Override
     void reloadPalette() {
         PaletteFactory.createPaletteRoot(mPaletteRoot, mLayoutEditor.getTargetData());
@@ -1474,36 +1476,12 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
     }
 
     private void onDensityChange() {
-        // because mDensity triggers onDensityChange at each modification, calling setText()
-        // will trigger notifications, and we don't want that.
-        if (mDisableUpdates == true) {
-            return;
-        }
-
-        // update the current config
-        String value = mDensity.getText();
-
-        // empty string, means no qualifier.
-        if (value.length() == 0) {
-            mCurrentConfig.setPixelDensityQualifier(null);
+        int index = mDensity.getSelectionIndex();
+        if (index != 0) {
+            mCurrentConfig.setPixelDensityQualifier((new PixelDensityQualifier(
+                Density.getByIndex(index-1))));
         } else {
-            try {
-                PixelDensityQualifier qualifier = PixelDensityQualifier.getQualifier(
-                        PixelDensityQualifier.getFolderSegment(Integer.parseInt(value)));
-                if (qualifier != null) {
-                    mCurrentConfig.setPixelDensityQualifier(qualifier);
-                } else {
-                    // Failure! Looks like the value is wrong (for instance a one letter string).
-                    // We do nothing in this case.
-                    return;
-                }
-            } catch (NumberFormatException e) {
-                // Looks like the code is not a number. This should not happen since the text
-                // field has a VerifyListener that prevents it.
-                // We do nothing in this case.
-                mDensityIcon.setImage(mErrorImage);
-                return;
-            }
+            mCurrentConfig.setPixelDensityQualifier(null);
         }
 
         // look for a file to open/create
@@ -1612,11 +1590,11 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         if (mEditedFile == null || mEditedConfig == null) {
             return;
         }
-        
+
         // get the resources of the file's project.
         ProjectResources resources = ResourceManager.getInstance().getProjectResources(
                 mEditedFile.getProject());
-        
+
         // from the resources, look for a matching file
         ResourceFile match = null;
         if (resources != null) {
@@ -1643,7 +1621,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
 
             // update the configuration icons with the new edited config.
             setConfiguration(mEditedConfig, false /*force*/);
-            
+
             // enable the create button if the current and edited config are not equals
             mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false);
 
@@ -1653,7 +1631,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         } else {
             // update the configuration icons with the new edited config.
             displayConfigError();
-            
+
             // enable the Create button
             mCreateButton.setEnabled(true);
 
@@ -1661,7 +1639,8 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             String message = String.format(
                     "No resources match the configuration\n \n\t%1$s\n \nChange the configuration or create:\n \n\tres/%2$s/%3$s\n \nYou can also click the 'Create' button above.",
                     mCurrentConfig.toDisplayString(),
-                    mCurrentConfig.getFolderName(ResourceFolderType.LAYOUT),
+                    mCurrentConfig.getFolderName(ResourceFolderType.LAYOUT,
+                            Sdk.getCurrent().getTarget(mEditedFile.getProject())),
                     mEditedFile.getName());
             showErrorInEditor(message);
         }
@@ -1671,7 +1650,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         int themeIndex = mThemeCombo.getSelectionIndex();
         if (themeIndex != -1) {
             String theme = mThemeCombo.getItem(themeIndex);
-            
+
             if (theme.equals(THEME_SEPARATOR)) {
                 mThemeCombo.select(0);
             }
@@ -1745,7 +1724,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                     showErrorInEditor("The project target is not set.");
                     return;
                 }
-                
+
                 AndroidTargetData data = currentSdk.getTargetData(target);
                 if (data == null) {
                     // It can happen that the workspace refreshes while the SDK is loading its
@@ -1774,67 +1753,77 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
 
                 if (bridge.bridge != null) { // bridge can never be null.
                     ResourceManager resManager = ResourceManager.getInstance();
-    
+
                     ProjectResources projectRes = resManager.getProjectResources(iProject);
                     if (projectRes == null) {
                         return;
                     }
-    
+
                     // get the resources of the file's project.
                     if (mConfiguredProjectRes == null) {
                         // make sure they are loaded
                         projectRes.loadAll();
-    
+
                         // get the project resource values based on the current config
                         mConfiguredProjectRes = projectRes.getConfiguredResources(mCurrentConfig);
                     }
-    
+
                     // get the framework resources
                     Map<String, Map<String, IResourceValue>> frameworkResources =
                         getConfiguredFrameworkResources();
-    
+
                     if (mConfiguredProjectRes != null && frameworkResources != null) {
                         if (mProjectCallback == null) {
                             mProjectCallback = new ProjectCallback(
                                     bridge.classLoader, projectRes, iProject);
                         }
-    
+
                         if (mLogger == null) {
                             mLogger = new ILayoutLog() {
                                 public void error(String message) {
                                     AdtPlugin.printErrorToConsole(mEditedFile.getName(), message);
                                 }
-    
+
                                 public void error(Throwable error) {
                                     String message = error.getMessage();
                                     if (message == null) {
                                         message = error.getClass().getName();
                                     }
-    
+
                                     PrintStream ps = new PrintStream(AdtPlugin.getErrorStream());
                                     error.printStackTrace(ps);
                                 }
-    
+
                                 public void warning(String message) {
                                     AdtPlugin.printToConsole(mEditedFile.getName(), message);
                                 }
                             };
                         }
-    
+
                         // get the selected theme
                         int themeIndex = mThemeCombo.getSelectionIndex();
                         if (themeIndex != -1) {
                             String theme = mThemeCombo.getItem(themeIndex);
-                            
+
                             // Compute the layout
                             UiElementPullParser parser = new UiElementPullParser(getModel());
                             Rectangle rect = getBounds();
                             boolean isProjectTheme = themeIndex >= mPlatformThemeCount;
 
                             // FIXME pass the density/dpi from somewhere (resource config or skin).
+                            // For now, get it from the config
+                            int density = Density.MEDIUM.getDpiValue();
+                            PixelDensityQualifier qual = mCurrentConfig.getPixelDensityQualifier();
+                            if (qual != null) {
+                                int d = qual.getValue().getDpiValue();
+                                if (d > 0) {
+                                    density = d;
+                                }
+                            }
+
                             ILayoutResult result = computeLayout(bridge, parser,
                                     iProject /* projectKey */,
-                                    rect.width, rect.height, 160, 160.f, 160.f, 
+                                    rect.width, rect.height, density, density, density,
                                     theme, isProjectTheme,
                                     mConfiguredProjectRes, frameworkResources, mProjectCallback,
                                     mLogger);
@@ -1842,20 +1831,20 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                             // update the UiElementNode with the layout info.
                             if (result.getSuccess() == ILayoutResult.SUCCESS) {
                                 model.setEditData(result.getImage());
-    
+
                                 updateNodeWithBounds(result.getRootView());
                             } else {
                                 String message = result.getErrorMessage();
-    
+
                                 // Reset the edit data for all the nodes.
                                 resetNodeBounds(model);
-    
+
                                 if (message != null) {
                                     // set the error in the top element.
                                     model.setEditData(message);
                                 }
                             }
-    
+
                             model.refreshUi();
                         }
                     }
@@ -1951,11 +1940,11 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             // clear the cache in the bridge in case a bitmap/9-patch changed.
             IAndroidTarget target = Sdk.getCurrent().getTarget(mEditedFile.getProject());
             if (target != null) {
-                
+
                 AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
                 if (data != null) {
                     LayoutBridge bridge = data.getLayoutBridge();
-        
+
                     if (bridge.bridge != null) {
                         bridge.bridge.clearCaches(mEditedFile.getProject());
                     }
@@ -2007,28 +1996,28 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         ProjectResources frameworkProject = getFrameworkResources();
 
         mDisableUpdates = true;
-        
+
         // Reset stuff
         int selection = mThemeCombo.getSelectionIndex();
         mThemeCombo.removeAll();
         mPlatformThemeCount = 0;
         mLanguage.removeAll();
-        
+
         Set<String> languages = new HashSet<String>();
         ArrayList<String> themes = new ArrayList<String>();
-        
+
         // get the themes, and languages from the Framework.
         if (frameworkProject != null) {
             // get the configured resources for the framework
             Map<String, Map<String, IResourceValue>> frameworResources =
                 getConfiguredFrameworkResources();
-            
+
             if (frameworResources != null) {
                 // get the styles.
                 Map<String, IResourceValue> styles = frameworResources.get(
                         ResourceType.STYLE.getName());
-                
-                
+
+
                 // collect the themes out of all the styles.
                 for (IResourceValue value : styles.values()) {
                     String name = value.getName();
@@ -2040,11 +2029,11 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
 
                 // sort them and add them to the combo
                 Collections.sort(themes);
-                
+
                 for (String theme : themes) {
                     mThemeCombo.add(theme);
                 }
-                
+
                 mPlatformThemeCount = themes.size();
                 themes.clear();
             }
@@ -2054,7 +2043,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                 languages.addAll(frameworkLanguages);
             }
         }
-        
+
         // now get the themes and languages from the project.
         ProjectResources project = null;
         if (mEditedFile != null) {
@@ -2062,7 +2051,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
 
             // in cases where the opened file is not linked to a project, this could be null.
             if (project != null) {
-                // get the configured resources for the project 
+                // get the configured resources for the project
                 if (mConfiguredProjectRes == null) {
                     // make sure they are loaded
                     project.loadAll();
@@ -2070,12 +2059,12 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                     // get the project resource values based on the current config
                     mConfiguredProjectRes = project.getConfiguredResources(mCurrentConfig);
                 }
-                
+
                 if (mConfiguredProjectRes != null) {
                     // get the styles.
                     Map<String, IResourceValue> styleMap = mConfiguredProjectRes.get(
                             ResourceType.STYLE.getName());
-                    
+
                     if (styleMap != null) {
                         // collect the themes out of all the styles, ie styles that extend,
                         // directly or indirectly a platform theme.
@@ -2089,9 +2078,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                         if (mPlatformThemeCount > 0 && themes.size() > 0) {
                             mThemeCombo.add(THEME_SEPARATOR);
                         }
-                        
+
                         Collections.sort(themes);
-                        
+
                         for (String theme : themes) {
                             mThemeCombo.add(theme);
                         }
@@ -2110,7 +2099,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
         for (String language : languages) {
             mLanguage.add(language);
         }
-        
+
         mDisableUpdates = false;
 
         // and update the Region UI based on the current language
@@ -2143,7 +2132,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
     private boolean isTheme(IResourceValue value, Map<String, IResourceValue> styleMap) {
         if (value instanceof IStyleResourceValue) {
             IStyleResourceValue style = (IStyleResourceValue)value;
-            
+
             boolean frameworkStyle = false;
             String parentStyle = style.getParentStyle();
             if (parentStyle == null) {
@@ -2160,13 +2149,13 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                 if (parentStyle.startsWith("@")) {
                     parentStyle = parentStyle.substring(1);
                 }
-                
+
                 // check for framework identifier.
                 if (parentStyle.startsWith("android:")) {
                     frameworkStyle = true;
                     parentStyle = parentStyle.substring("android:".length());
                 }
-                
+
                 // at this point we could have the format style/<name>. we want only the name
                 if (parentStyle.startsWith("style/")) {
                     parentStyle = parentStyle.substring("style/".length());
@@ -2232,7 +2221,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             mDisableUpdates = false;
         }
     }
-    
+
     private Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources() {
         if (mConfiguredFrameworkRes == null) {
             ProjectResources frameworkRes = getFrameworkResources();
@@ -2244,7 +2233,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             // get the framework resource values based on the current config
             mConfiguredFrameworkRes = frameworkRes.getConfiguredResources(mCurrentConfig);
         }
-        
+
         return mConfiguredFrameworkRes;
     }
 
@@ -2256,14 +2245,15 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             @Override
             protected IStatus run(IProgressMonitor monitor) {
                 // get the folder name
-                String folderName = config.getFolderName(ResourceFolderType.LAYOUT);
+                String folderName = config.getFolderName(ResourceFolderType.LAYOUT,
+                        Sdk.getCurrent().getTarget(mEditedFile.getProject()));
                 try {
-                    
+
                     // look to see if it exists.
                     // get the res folder
                     IFolder res = (IFolder)mEditedFile.getParent().getParent();
                     String path = res.getLocation().toOSString();
-                    
+
                     File newLayoutFolder = new File(path + File.separator + folderName);
                     if (newLayoutFolder.isFile()) {
                         // this should not happen since aapt would have complained
@@ -2271,34 +2261,34 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                         // happen.
                         String message = String.format("File 'res/%1$s' is in the way!",
                                 folderName);
-                        
+
                         AdtPlugin.displayError("Layout Creation", message);
-                        
+
                         return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message);
                     } else if (newLayoutFolder.exists() == false) {
                         // create it.
                         newLayoutFolder.mkdir();
                     }
-                    
+
                     // now create the file
                     File newLayoutFile = new File(newLayoutFolder.getAbsolutePath() +
                                 File.separator + mEditedFile.getName());
 
                     newLayoutFile.createNewFile();
-                    
+
                     InputStream input = mEditedFile.getContents();
-                    
+
                     FileOutputStream fos = new FileOutputStream(newLayoutFile);
-                    
+
                     byte[] data = new byte[512];
                     int count;
                     while ((count = input.read(data)) != -1) {
                         fos.write(data, 0, count);
                     }
-                    
+
                     input.close();
                     fos.close();
-                    
+
                     // refreshes the res folder to show up the new
                     // layout folder (if needed) and the file.
                     // We use a progress monitor to catch the end of the refresh
@@ -2346,27 +2336,27 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
                     String message = String.format(
                             "Failed to create File 'res/%1$s/%2$s' : %3$s",
                             folderName, mEditedFile.getName(), e2.getMessage());
-                    
+
                     AdtPlugin.displayError("Layout Creation", message);
-                    
+
                     return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
                             message, e2);
                 } catch (CoreException e2) {
                     String message = String.format(
                             "Failed to create File 'res/%1$s/%2$s' : %3$s",
                             folderName, mEditedFile.getName(), e2.getMessage());
-                    
+
                     AdtPlugin.displayError("Layout Creation", message);
 
                     return e2.getStatus();
                 }
-                
+
                 return Status.OK_STATUS;
 
             }
         }.schedule();
     }
-    
+
     /**
      * Returns a {@link ProjectResources} for the framework resources.
      * @return the framework resources or null if not found.
@@ -2376,10 +2366,10 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             Sdk currentSdk = Sdk.getCurrent();
             if (currentSdk != null) {
                 IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
-    
+
                 if (target != null) {
                     AndroidTargetData data = currentSdk.getTargetData(target);
-                    
+
                     if (data != null) {
                         return data.getFrameworkResources();
                     }
@@ -2389,7 +2379,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
 
         return null;
     }
-    
+
     /**
      * Computes a layout by calling the correct computeLayout method of ILayoutBridge based on
      * the implementation API level.
@@ -2402,12 +2392,12 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
             Map<String, Map<String, IResourceValue>> projectResources,
             Map<String, Map<String, IResourceValue>> frameworkResources,
             IProjectCallback projectCallback, ILayoutLog logger) {
-        
+
         if (bridge.apiLevel >= 3) {
             // newer api with boolean for separation of project/framework theme,
             // and density support.
             return bridge.bridge.computeLayout(layoutDescription,
-                    projectKey, screenWidth, screenHeight, density, xdpi, ydpi, 
+                    projectKey, screenWidth, screenHeight, density, xdpi, ydpi,
                     themeName, isProjectTheme,
                     projectResources, frameworkResources, projectCallback,
                     logger);
index 5cd527c..a55f1d0 100644 (file)
@@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQua
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.TrayDialog;
@@ -40,22 +41,26 @@ class LayoutCreatorDialog extends TrayDialog {
 
     private ConfigurationSelector mSelector;
     private Composite mStatusComposite;
-    private Label mStatusLabel; 
+    private Label mStatusLabel;
     private Label mStatusImage;
 
     private final FolderConfiguration mConfig = new FolderConfiguration();
     private final String mFileName;
+    private final IAndroidTarget mTarget;
 
     /**
      * Creates a dialog, and init the UI from a {@link FolderConfiguration}.
      * @param parentShell the parent {@link Shell}.
      * @param config The starting configuration.
      */
-    LayoutCreatorDialog(Shell parentShell, String fileName, FolderConfiguration config) {
+    LayoutCreatorDialog(Shell parentShell, String fileName, IAndroidTarget target,
+            FolderConfiguration config) {
         super(parentShell);
 
-        mFileName = fileName;        
-        // FIXME: add some data to know what configurations already exist. 
+        mFileName = fileName;
+        mTarget = target;
+
+        // FIXME: add some data to know what configurations already exist.
         mConfig.set(config);
     }
 
@@ -67,22 +72,22 @@ class LayoutCreatorDialog extends TrayDialog {
 
         new Label(top, SWT.NONE).setText(
                 String.format("Configuration for the alternate version of %1$s", mFileName));
-        
+
         mSelector = new ConfigurationSelector(top);
         mSelector.setConfiguration(mConfig);
-        
+
         // parent's layout is a GridLayout as specified in the javadoc.
         GridData gd = new GridData();
         gd.widthHint = ConfigurationSelector.WIDTH_HINT;
         gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
         mSelector.setLayoutData(gd);
-        
+
         // add a listener to check on the validity of the FolderConfiguration as
         // they are built.
         mSelector.setOnChangeListener(new Runnable() {
             public void run() {
                 ConfigurationState state = mSelector.getState();
-                
+
                 switch (state) {
                     case OK:
                         mSelector.getConfiguration(mConfig);
@@ -125,16 +130,16 @@ class LayoutCreatorDialog extends TrayDialog {
 
         return top;
     }
-    
+
     public void getConfiguration(FolderConfiguration config) {
         config.set(mConfig);
     }
-    
+
     /**
      * resets the status label to show the file that will be created.
      */
     private void resetStatus() {
         mStatusLabel.setText(String.format("New File: res/%1$s/%2$s",
-                mConfig.getFolderName(ResourceFolderType.LAYOUT), mFileName));
+                mConfig.getFolderName(ResourceFolderType.LAYOUT, mTarget), mFileName));
     }
 }
index 8b61e27..315f456 100644 (file)
@@ -36,7 +36,6 @@ import com.android.sdkuilib.internal.widgets.AvdSelector.IAvdFilter;
 
 import org.eclipse.jface.dialogs.Dialog;
 import org.eclipse.jface.dialogs.IDialogConstants;
-import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.jface.viewers.ILabelProviderListener;
 import org.eclipse.jface.viewers.IStructuredContentProvider;
 import org.eclipse.jface.viewers.ITableLabelProvider;
@@ -68,12 +67,6 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
 
     private final static int ICON_WIDTH = 16;
 
-    private final static String PREFS_COL_SERIAL = "deviceChooser.serial"; //$NON-NLS-1$
-    private final static String PREFS_COL_STATE  = "deviceChooser.state"; //$NON-NLS-1$
-    private final static String PREFS_COL_AVD     = "deviceChooser.avd"; //$NON-NLS-1$
-    private final static String PREFS_COL_TARGET = "deviceChooser.target"; //$NON-NLS-1$
-    private final static String PREFS_COL_DEBUG  = "deviceChooser.debug"; //$NON-NLS-1$
-
     private Table mDeviceTable;
     private TableViewer mViewer;
     private AvdSelector mPreferredAvdSelector;
@@ -334,7 +327,6 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
         layout.marginLeft = 30;
         offsetComp.setLayout(layout);
 
-        IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
         mDeviceTable = new Table(offsetComp, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
         GridData gd;
         mDeviceTable.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
@@ -345,23 +337,23 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
 
         TableHelper.createTableColumn(mDeviceTable, "Serial Number",
                 SWT.LEFT, "AAA+AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
-                PREFS_COL_SERIAL, store);
+                null /* prefs name */, null /* prefs store */);
 
         TableHelper.createTableColumn(mDeviceTable, "AVD Name",
-                SWT.LEFT, "engineering", //$NON-NLS-1$
-                PREFS_COL_AVD, store);
+                SWT.LEFT, "AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
+                null /* prefs name */, null /* prefs store */);
 
         TableHelper.createTableColumn(mDeviceTable, "Target",
                 SWT.LEFT, "AAA+Android 9.9.9", //$NON-NLS-1$
-                PREFS_COL_TARGET, store);
+                null /* prefs name */, null /* prefs store */);
 
         TableHelper.createTableColumn(mDeviceTable, "Debug",
                 SWT.LEFT, "Debug", //$NON-NLS-1$
-                PREFS_COL_DEBUG, store);
+                null /* prefs name */, null /* prefs store */);
 
         TableHelper.createTableColumn(mDeviceTable, "State",
                 SWT.LEFT, "bootloader", //$NON-NLS-1$
-                PREFS_COL_STATE, store);
+                null /* prefs name */, null /* prefs store */);
 
         // create the viewer for it
         mViewer = new TableViewer(mDeviceTable);
index 2cdf3be..235ba7c 100644 (file)
@@ -41,17 +41,17 @@ import java.util.zip.ZipOutputStream;
  * Export helper for project.
  */
 public final class ExportHelper {
-    
+
     private static IExportCallback sCallback;
 
     public interface IExportCallback {
         void startExportWizard(IProject project);
     }
-    
+
     public static void setCallback(IExportCallback callback) {
         sCallback = callback;
     }
-    
+
     public static void startExportWizard(IProject project) {
         if (sCallback != null) {
             sCallback.startExportWizard(project);
@@ -69,30 +69,30 @@ public final class ExportHelper {
         IFolder outputFolder = BaseProjectHelper.getOutputFolder(project);
         if (outputFolder != null) {
             IPath binLocation = outputFolder.getLocation();
-    
+
             // make the full path to the package
             String fileName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
-    
+
             File file = new File(binLocation.toOSString() + File.separator + fileName);
-    
+
             if (file.exists() == false || file.isFile() == false) {
-                MessageDialog.openInformation(Display.getCurrent().getActiveShell(),
+                MessageDialog.openError(Display.getCurrent().getActiveShell(),
                         "Android IDE Plug-in",
                         String.format("Failed to export %1$s: %2$s doesn't exist!",
                                 project.getName(), file.getPath()));
                 return;
             }
-    
+
             // ok now pop up the file save window
             FileDialog fileDialog = new FileDialog(shell, SWT.SAVE);
-    
+
             fileDialog.setText("Export Project");
             fileDialog.setFileName(fileName);
-    
+
             String saveLocation = fileDialog.open();
             if (saveLocation != null) {
                 // get the stream from the original file
-                
+
                 ZipInputStream zis = null;
                 ZipOutputStream zos = null;
                 FileInputStream input = null;
@@ -116,8 +116,8 @@ public final class ExportHelper {
                             // pass
                         }
                     }
-                    
-                    MessageDialog.openInformation(shell, "Android IDE Plug-in",
+
+                    MessageDialog.openError(shell, "Android IDE Plug-in",
                             String.format("Failed to export %1$s: %2$s doesn't exist!",
                                     project.getName(), file.getPath()));
                     return;
@@ -125,20 +125,20 @@ public final class ExportHelper {
 
                 try {
                     ZipEntry entry;
-                    
+
                     byte[] buffer = new byte[4096];
 
                     while ((entry = zis.getNextEntry()) != null) {
                         String name = entry.getName();
-                        
+
                         // do not take directories or anything inside the META-INF folder since
                         // we want to strip the signature.
                         if (entry.isDirectory() || name.startsWith("META-INF/")) { //$NON-NL1$
                             continue;
                         }
-            
+
                         ZipEntry newEntry;
-            
+
                         // Preserve the STORED method of the input entry.
                         if (entry.getMethod() == JarEntry.STORED) {
                             newEntry = new JarEntry(entry);
@@ -146,12 +146,12 @@ public final class ExportHelper {
                             // Create a new entry so that the compressed len is recomputed.
                             newEntry = new JarEntry(name);
                         }
-                        
+
                         // add the entry to the jar archive
                         zos.putNextEntry(newEntry);
 
                         // read the content of the entry from the input stream, and write it into the archive.
-                        int count; 
+                        int count;
                         while ((count = zis.read(buffer)) != -1) {
                             zos.write(buffer, 0, count);
                         }
@@ -159,11 +159,10 @@ public final class ExportHelper {
                         // close the entry for this file
                         zos.closeEntry();
                         zis.closeEntry();
-
                     }
-    
+
                 } catch (IOException e) {
-                    MessageDialog.openInformation(shell, "Android IDE Plug-in",
+                    MessageDialog.openError(shell, "Android IDE Plug-in",
                             String.format("Failed to export %1$s: %2$s",
                                     project.getName(), e.getMessage()));
                 } finally {
@@ -178,9 +177,19 @@ public final class ExportHelper {
                         // pass
                     }
                 }
+
+                // this is unsigned export. Let's tell the developers to run zip align
+                MessageDialog.openWarning(shell, "Android IDE Plug-in", String.format(
+                        "An unsigned package of the application was saved at\n%1$s\n\n" +
+                        "Before publishing the application you will need to:\n" +
+                        "- Sign the application with your release key,\n" +
+                        "- run zipalign on the signed package. ZipAlign is located in <SDK>/tools/\n\n" +
+                        "Aligning applications allows Android to use application resources\n" +
+                        "more efficiently.", saveLocation));
+
             }
         } else {
-            MessageDialog.openInformation(shell, "Android IDE Plug-in",
+            MessageDialog.openError(shell, "Android IDE Plug-in",
                     String.format("Failed to export %1$s: Could not get project output location",
                             project.getName()));
         }
index a3fdb7d..072b1b8 100644 (file)
@@ -420,7 +420,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
             // recreate the res path from the current configuration
             mConfigSelector.getConfiguration(mTempConfig);
             StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
-            sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES));
+            sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES, mProject));
             sb.append('/');
 
             String newPath = sb.toString();
index 4cd6085..c37fb6b 100644 (file)
@@ -866,7 +866,7 @@ public class ExtractStringRefactoring extends Refactoring {
                         append("\">").                                      //$NON-NLS-1$
                         append(tokenString).
                         append("</string>\n");                              //$NON-NLS-1$
-            content.append("<resources>\n");                                //$NON-NLS-1$
+            content.append("</resources>\n");                                //$NON-NLS-1$
 
             edit = new InsertEdit(0, content.toString());
             editGroup = new TextEditGroup("Create <string> in new XML file", edit);
index c624035..f570bac 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -33,9 +34,9 @@ public final class CountryCodeQualifier extends ResourceQualifier {
     private final static Pattern sCountryCodePattern = Pattern.compile("^mcc(\\d{3})$");//$NON-NLS-1$
 
     private int mCode = DEFAULT_CODE;
-    
+
     public static final String NAME = "Mobile Country Code";
-    
+
     /**
      * Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
      * <code>null</code> is returned.
@@ -54,15 +55,15 @@ public final class CountryCodeQualifier extends ResourceQualifier {
                 // looks like the string we extracted wasn't a valid number.
                 return null;
             }
-            
+
             CountryCodeQualifier qualifier = new CountryCodeQualifier();
             qualifier.mCode = code;
             return qualifier;
         }
-        
+
         return null;
     }
-    
+
     /**
      * Returns the folder name segment for the given value. This is equivalent to calling
      * {@link #toString()} on a {@link CountryCodeQualifier} object.
@@ -72,29 +73,29 @@ public final class CountryCodeQualifier extends ResourceQualifier {
         if (code != DEFAULT_CODE && code >= 100 && code <=999) { // code is 3 digit.) {
             return String.format("mcc%1$d", code); //$NON-NLS-1$
         }
-        
+
         return ""; //$NON-NLS-1$
     }
-    
+
     public int getCode() {
         return mCode;
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return "Country Code";
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("mcc"); //$NON-NLS-1$
     }
-    
+
     @Override
     public boolean isValid() {
         return mCode != DEFAULT_CODE;
@@ -107,29 +108,29 @@ public final class CountryCodeQualifier extends ResourceQualifier {
             config.setCountryCodeQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof CountryCodeQualifier) {
             return mCode == ((CountryCodeQualifier)qualifier).mCode;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public int hashCode() {
         return mCode;
     }
-    
+
     /**
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         return getFolderSegment(mCode);
     }
 
@@ -138,7 +139,7 @@ public final class CountryCodeQualifier extends ResourceQualifier {
         if (mCode != DEFAULT_CODE) {
             return String.format("MCC %1$d", mCode);
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
index aea146b..23a6440 100644 (file)
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.core.resources.IProject;
 
 
 /**
@@ -27,20 +31,23 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
     public final static String QUALIFIER_SEP = "-"; //$NON-NLS-1$
 
     private final ResourceQualifier[] mQualifiers = new ResourceQualifier[INDEX_COUNT];
-    
-    private final static int INDEX_COUNTRY_CODE = 0;
-    private final static int INDEX_NETWORK_CODE = 1;
-    private final static int INDEX_LANGUAGE = 2;
-    private final static int INDEX_REGION = 3;
-    private final static int INDEX_SCREEN_ORIENTATION = 4;
-    private final static int INDEX_PIXEL_DENSITY = 5;
-    private final static int INDEX_TOUCH_TYPE = 6;
-    private final static int INDEX_KEYBOARD_STATE = 7;
-    private final static int INDEX_TEXT_INPUT_METHOD = 8;
-    private final static int INDEX_NAVIGATION_METHOD = 9;
-    private final static int INDEX_SCREEN_DIMENSION = 10;
-    private final static int INDEX_COUNT = 11;
-    
+
+    private final static int INDEX_COUNTRY_CODE       = 0;
+    private final static int INDEX_NETWORK_CODE       = 1;
+    private final static int INDEX_LANGUAGE           = 2;
+    private final static int INDEX_REGION             = 3;
+    private final static int INDEX_SCREEN_SIZE        = 4;
+    private final static int INDEX_SCREEN_RATIO       = 5;
+    private final static int INDEX_SCREEN_ORIENTATION = 6;
+    private final static int INDEX_PIXEL_DENSITY      = 7;
+    private final static int INDEX_TOUCH_TYPE         = 8;
+    private final static int INDEX_KEYBOARD_STATE     = 9;
+    private final static int INDEX_TEXT_INPUT_METHOD  = 10;
+    private final static int INDEX_NAVIGATION_METHOD  = 11;
+    private final static int INDEX_SCREEN_DIMENSION   = 12;
+    private final static int INDEX_VERSION            = 13;
+    private final static int INDEX_COUNT              = 14;
+
     /**
      * Sets the config from the qualifiers of a given <var>config</var>.
      * @param config
@@ -62,7 +69,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
             }
         }
     }
-    
+
     /**
      * Returns the first invalid qualifier, or <code>null<code> if they are all valid (or if none
      * exists).
@@ -73,11 +80,11 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                 return mQualifiers[i];
             }
         }
-        
+
         // all allocated qualifiers are valid, we return null.
         return null;
     }
-    
+
     /**
      * Returns whether the Region qualifier is valid. Region qualifier can only be present if a
      * Language qualifier is present as well.
@@ -90,7 +97,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
 
         return true;
     }
-    
+
     /**
      * Adds a qualifier to the {@link FolderConfiguration}
      * @param qualifier the {@link ResourceQualifier} to add.
@@ -104,6 +111,10 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
             mQualifiers[INDEX_LANGUAGE] = qualifier;
         } else if (qualifier instanceof RegionQualifier) {
             mQualifiers[INDEX_REGION] = qualifier;
+        } else if (qualifier instanceof ScreenSizeQualifier) {
+            mQualifiers[INDEX_SCREEN_SIZE] = qualifier;
+        } else if (qualifier instanceof ScreenRatioQualifier) {
+            mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
         } else if (qualifier instanceof ScreenOrientationQualifier) {
             mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
         } else if (qualifier instanceof PixelDensityQualifier) {
@@ -118,9 +129,11 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
             mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier;
         } else if (qualifier instanceof ScreenDimensionQualifier) {
             mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier;
+        } else if (qualifier instanceof VersionQualifier) {
+            mQualifiers[INDEX_VERSION] = qualifier;
         }
     }
-    
+
     /**
      * Removes a given qualifier from the {@link FolderConfiguration}.
      * @param qualifier the {@link ResourceQualifier} to remove.
@@ -133,7 +146,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
             }
         }
     }
-    
+
     public void setCountryCodeQualifier(CountryCodeQualifier qualifier) {
         mQualifiers[INDEX_COUNTRY_CODE] = qualifier;
     }
@@ -166,6 +179,22 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
         return (RegionQualifier)mQualifiers[INDEX_REGION];
     }
 
+    public void setScreenSizeQualifier(ScreenSizeQualifier qualifier) {
+        mQualifiers[INDEX_SCREEN_SIZE] = qualifier;
+    }
+
+    public ScreenSizeQualifier getScreenSizeQualifier() {
+        return (ScreenSizeQualifier)mQualifiers[INDEX_SCREEN_SIZE];
+    }
+
+    public void setScreenRatioQualifier(ScreenRatioQualifier qualifier) {
+        mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
+    }
+
+    public ScreenRatioQualifier getScreenRatioQualifier() {
+        return (ScreenRatioQualifier)mQualifiers[INDEX_SCREEN_RATIO];
+    }
+
     public void setScreenOrientationQualifier(ScreenOrientationQualifier qualifier) {
         mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
     }
@@ -205,7 +234,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
     public TextInputMethodQualifier getTextInputMethodQualifier() {
         return (TextInputMethodQualifier)mQualifiers[INDEX_TEXT_INPUT_METHOD];
     }
-    
+
     public void setNavigationMethodQualifier(NavigationMethodQualifier qualifier) {
         mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier;
     }
@@ -213,7 +242,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
     public NavigationMethodQualifier getNavigationMethodQualifier() {
         return (NavigationMethodQualifier)mQualifiers[INDEX_NAVIGATION_METHOD];
     }
-    
+
     public void setScreenDimensionQualifier(ScreenDimensionQualifier qualifier) {
         mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier;
     }
@@ -222,6 +251,14 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
         return (ScreenDimensionQualifier)mQualifiers[INDEX_SCREEN_DIMENSION];
     }
 
+    public void setVersionQualifier(VersionQualifier qualifier) {
+        mQualifiers[INDEX_VERSION] = qualifier;
+    }
+
+    public VersionQualifier getVersionQualifier() {
+        return (VersionQualifier)mQualifiers[INDEX_VERSION];
+    }
+
     /**
      * Returns whether an object is equals to the receiver.
      */
@@ -230,7 +267,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
         if (obj == this) {
             return true;
         }
-        
+
         if (obj instanceof FolderConfiguration) {
             FolderConfiguration fc = (FolderConfiguration)obj;
             for (int i = 0 ; i < INDEX_COUNT ; i++) {
@@ -247,7 +284,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
 
             return true;
         }
-        
+
         return false;
     }
 
@@ -255,7 +292,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
     public int hashCode() {
         return toString().hashCode();
     }
-    
+
     /**
      * Returns whether the Configuration has only default values.
      */
@@ -265,52 +302,49 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                 return false;
             }
         }
-        
+
         return true;
     }
-    
+
     /**
      * Returns the name of a folder with the configuration.
      */
-    public String getFolderName(ResourceFolderType folder) {
+    public String getFolderName(ResourceFolderType folder, IAndroidTarget target) {
         StringBuilder result = new StringBuilder(folder.getName());
-        
+
         for (ResourceQualifier qualifier : mQualifiers) {
             if (qualifier != null) {
                 result.append(QUALIFIER_SEP);
-                result.append(qualifier.toString());
+                result.append(qualifier.getFolderSegment(target));
             }
         }
-        
+
         return result.toString();
     }
-    
+
     /**
-     * Returns a string valid for usage in a folder name, or <code>null</code> if the configuration
-     * is default.
+     * Returns the name of a folder with the configuration.
      */
-    @Override
-    public String toString() {
-        StringBuilder result = null;
-        
-        for (ResourceQualifier irq : mQualifiers) {
-            if (irq != null) {
-                if (result == null) {
-                    result = new StringBuilder();
-                } else {
-                    result.append(QUALIFIER_SEP);
-                }
-                result.append(irq.toString());
+    public String getFolderName(ResourceFolderType folder, IProject project) {
+        IAndroidTarget target = null;
+        if (project != null) {
+            Sdk currentSdk = Sdk.getCurrent();
+            if (currentSdk != null) {
+                target = currentSdk.getTarget(project);
             }
         }
-        
-        if (result != null) {
-            return result.toString();
-        } else {
-            return null;
-        }
+
+        return getFolderName(folder, target);
     }
-    
+
+    /**
+     * Returns {@link #toDisplayString()}.
+     */
+    @Override
+    public String toString() {
+        return toDisplayString();
+    }
+
     /**
      * Returns a string valid for display purpose.
      */
@@ -322,7 +356,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
         StringBuilder result = null;
         int index = 0;
         ResourceQualifier qualifier = null;
-        
+
         // pre- language/region qualifiers
         while (index < INDEX_LANGUAGE) {
             qualifier = mQualifiers[index++];
@@ -333,10 +367,10 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                     result.append(", "); //$NON-NLS-1$
                 }
                 result.append(qualifier.getStringValue());
-                
+
             }
         }
-        
+
         // process the language/region qualifier in a custom way, if there are both non null.
         if (mQualifiers[INDEX_LANGUAGE] != null && mQualifiers[INDEX_REGION] != null) {
             String language = mQualifiers[INDEX_LANGUAGE].getStringValue();
@@ -348,10 +382,10 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                 result.append(", "); //$NON-NLS-1$
             }
             result.append(String.format("%s_%s", language, region)); //$NON-NLS-1$
-            
+
             index += 2;
         }
-        
+
         // post language/region qualifiers.
         while (index < INDEX_COUNT) {
             qualifier = mQualifiers[index++];
@@ -362,7 +396,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                     result.append(", "); //$NON-NLS-1$
                 }
                 result.append(qualifier.getStringValue());
-                
+
             }
         }
 
@@ -377,12 +411,12 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
             }
             return -1;
         }
-        
+
         // now we compare the qualifiers
         for (int i = 0 ; i < INDEX_COUNT; i++) {
             ResourceQualifier qualifier1 = mQualifiers[i];
             ResourceQualifier qualifier2 = folderConfig.mQualifiers[i];
-            
+
             if (qualifier1 == null) {
                 if (qualifier2 == null) {
                     continue;
@@ -394,16 +428,16 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                     return 1;
                 } else {
                     int result = qualifier1.compareTo(qualifier2);
-                    
+
                     if (result == 0) {
                         continue;
                     }
-                    
+
                     return result;
                 }
             }
         }
-        
+
         // if we arrive here, all the qualifier matches
         return 0;
     }
@@ -421,11 +455,11 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
      */
     public int match(FolderConfiguration referenceConfig) {
         int matchCount = 0;
-        
+
         for (int i = 0 ; i < INDEX_COUNT ; i++) {
             ResourceQualifier testQualifier = mQualifiers[i];
             ResourceQualifier referenceQualifier = referenceConfig.mQualifiers[i];
-            
+
             // we only care if testQualifier is non null. If it's null, it's a match but
             // without increasing the matchCount.
             if (testQualifier != null) {
@@ -434,7 +468,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                 } else if (testQualifier.equals(referenceQualifier) == false) {
                     return -1;
                 }
-                
+
                 // the qualifier match, increment the count
                 matchCount++;
             }
@@ -454,10 +488,10 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                 return i;
             }
         }
-        
+
         return -1;
     }
-    
+
     /**
      * Create default qualifiers.
      */
@@ -466,6 +500,8 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
         mQualifiers[INDEX_NETWORK_CODE] = new NetworkCodeQualifier();
         mQualifiers[INDEX_LANGUAGE] = new LanguageQualifier();
         mQualifiers[INDEX_REGION] = new RegionQualifier();
+        mQualifiers[INDEX_SCREEN_SIZE] = new ScreenSizeQualifier();
+        mQualifiers[INDEX_SCREEN_RATIO] = new ScreenRatioQualifier();
         mQualifiers[INDEX_SCREEN_ORIENTATION] = new ScreenOrientationQualifier();
         mQualifiers[INDEX_PIXEL_DENSITY] = new PixelDensityQualifier();
         mQualifiers[INDEX_TOUCH_TYPE] = new TouchScreenQualifier();
@@ -473,6 +509,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
         mQualifiers[INDEX_TEXT_INPUT_METHOD] = new TextInputMethodQualifier();
         mQualifiers[INDEX_NAVIGATION_METHOD] = new NavigationMethodQualifier();
         mQualifiers[INDEX_SCREEN_DIMENSION] = new ScreenDimensionQualifier();
+        mQualifiers[INDEX_VERSION] = new VersionQualifier();
     }
 
     /**
@@ -485,7 +522,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                 count++;
             }
         }
-        
+
         ResourceQualifier[] array = new ResourceQualifier[count];
         int index = 0;
         for (int i = 0 ; i < INDEX_COUNT ; i++) {
@@ -493,7 +530,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
                 array[index++] = mQualifiers[i];
             }
         }
-        
+
         return array;
     }
 }
index 2acebcc..1aa9286 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -26,7 +27,7 @@ import org.eclipse.swt.graphics.Image;
  * Resource Qualifier for keyboard state.
  */
 public final class KeyboardStateQualifier extends ResourceQualifier {
-    
+
     public static final String NAME = "Keyboard State";
 
     private KeyboardState mValue = null;
@@ -37,15 +38,15 @@ public final class KeyboardStateQualifier extends ResourceQualifier {
     public static enum KeyboardState {
         EXPOSED("keysexposed", "Exposed"), //$NON-NLS-1$
         HIDDEN("keyshidden", "Hidden"); //$NON-NLS-1$
-        
+
         private String mValue;
         private String mDisplayValue;
-        
+
         private KeyboardState(String value, String displayValue) {
             mValue = value;
             mDisplayValue = displayValue;
         }
-        
+
         /**
          * Returns the enum for matching the provided qualifier value.
          * @param value The qualifier value.
@@ -57,25 +58,25 @@ public final class KeyboardStateQualifier extends ResourceQualifier {
                     return orient;
                 }
             }
-            
+
             return null;
         }
 
         public String getValue() {
             return mValue;
         }
-        
+
         public String getDisplayValue() {
             return mDisplayValue;
         }
-        
+
         public static int getIndex(KeyboardState value) {
             int i = 0;
             for (KeyboardState input : values()) {
                 if (value == input) {
                     return i;
                 }
-                
+
                 i++;
             }
 
@@ -105,27 +106,27 @@ public final class KeyboardStateQualifier extends ResourceQualifier {
     public KeyboardState getValue() {
         return mValue;
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return "Keyboard";
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("keyboard"); //$NON-NLS-1$
     }
-    
+
     @Override
     public boolean isValid() {
         return mValue != null;
     }
-    
+
     @Override
     public boolean checkAndSet(String value, FolderConfiguration config) {
         KeyboardState orientation = KeyboardState.getEnum(value);
@@ -135,10 +136,10 @@ public final class KeyboardStateQualifier extends ResourceQualifier {
             config.setKeyboardStateQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof KeyboardStateQualifier) {
@@ -153,19 +154,19 @@ public final class KeyboardStateQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.hashCode();
         }
-        
+
         return 0;
     }
-    
+
     /**
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         if (mValue != null) {
             return mValue.getValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 
@@ -174,7 +175,7 @@ public final class KeyboardStateQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.getDisplayValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
index 799fc06..a098e44 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -29,9 +30,9 @@ public final class LanguageQualifier extends ResourceQualifier {
     private final static Pattern sLanguagePattern = Pattern.compile("^[a-z]{2}$"); //$NON-NLS-1$
 
     public static final String NAME = "Language";
-    
+
     private String mValue;
-    
+
     /**
      * Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
      * <code>null</code> is returned.
@@ -42,12 +43,12 @@ public final class LanguageQualifier extends ResourceQualifier {
         if (sLanguagePattern.matcher(segment).matches()) {
             LanguageQualifier qualifier = new LanguageQualifier();
             qualifier.mValue = segment;
-            
+
             return qualifier;
         }
         return null;
     }
-    
+
     /**
      * Returns the folder name segment for the given value. This is equivalent to calling
      * {@link #toString()} on a {@link LanguageQualifier} object.
@@ -58,7 +59,7 @@ public final class LanguageQualifier extends ResourceQualifier {
         if (sLanguagePattern.matcher(segment).matches()) {
             return segment;
         }
-        
+
         return null;
     }
 
@@ -66,25 +67,25 @@ public final class LanguageQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue;
         }
-        
+
         return ""; //$NON-NLS-1$
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return NAME;
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("language"); //$NON-NLS-1$
     }
-    
+
     @Override
     public boolean isValid() {
         return mValue != null;
@@ -97,10 +98,10 @@ public final class LanguageQualifier extends ResourceQualifier {
             config.setLanguageQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof LanguageQualifier) {
@@ -109,7 +110,7 @@ public final class LanguageQualifier extends ResourceQualifier {
             }
             return mValue.equals(((LanguageQualifier)qualifier).mValue);
         }
-        
+
         return false;
     }
 
@@ -118,15 +119,15 @@ public final class LanguageQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.hashCode();
         }
-        
+
         return 0;
     }
-    
+
     /**
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         if (mValue != null) {
             return getFolderSegment(mValue);
         }
@@ -139,7 +140,7 @@ public final class LanguageQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue;
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
index 6315014..0a5c968 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -26,7 +27,7 @@ import org.eclipse.swt.graphics.Image;
  * Resource Qualifier for Navigation Method.
  */
 public final class NavigationMethodQualifier extends ResourceQualifier {
-    
+
     public static final String NAME = "Navigation Method";
 
     private NavigationMethod mValue;
@@ -39,15 +40,15 @@ public final class NavigationMethodQualifier extends ResourceQualifier {
         TRACKBALL("trackball", "Trackball"), //$NON-NLS-1$
         WHEEL("wheel", "Wheel"), //$NON-NLS-1$
         NONAV("nonav", "No Navigation"); //$NON-NLS-1$
-        
+
         private String mValue;
         private String mDisplay;
-        
+
         private NavigationMethod(String value, String display) {
             mValue = value;
             mDisplay = display;
         }
-        
+
         /**
          * Returns the enum for matching the provided qualifier value.
          * @param value The qualifier value.
@@ -59,14 +60,14 @@ public final class NavigationMethodQualifier extends ResourceQualifier {
                     return orient;
                 }
             }
-            
+
             return null;
         }
-        
+
         public String getValue() {
             return mValue;
         }
-        
+
         public String getDisplayValue() {
             return mDisplay;
         }
@@ -77,7 +78,7 @@ public final class NavigationMethodQualifier extends ResourceQualifier {
                 if (nav == value) {
                     return i;
                 }
-                
+
                 i++;
             }
 
@@ -95,7 +96,7 @@ public final class NavigationMethodQualifier extends ResourceQualifier {
             return null;
         }
     }
-    
+
     public NavigationMethodQualifier() {
         // pass
     }
@@ -107,18 +108,18 @@ public final class NavigationMethodQualifier extends ResourceQualifier {
     public NavigationMethod getValue() {
         return mValue;
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return "Navigation";
     }
 
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("navpad"); //$NON-NLS-1$
@@ -138,16 +139,16 @@ public final class NavigationMethodQualifier extends ResourceQualifier {
             config.setNavigationMethodQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof NavigationMethodQualifier) {
             return mValue == ((NavigationMethodQualifier)qualifier).mValue;
         }
-        
+
         return false;
     }
 
@@ -156,19 +157,19 @@ public final class NavigationMethodQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.hashCode();
         }
-        
+
         return 0;
     }
-    
+
     /**
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         if (mValue != null) {
             return mValue.getValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 
@@ -177,7 +178,7 @@ public final class NavigationMethodQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.getDisplayValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
index c7f7fad..32bd667 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -33,9 +34,9 @@ public final class NetworkCodeQualifier extends ResourceQualifier {
     private final static Pattern sNetworkCodePattern = Pattern.compile("^mnc(\\d{1,3})$"); //$NON-NLS-1$
 
     private int mCode = DEFAULT_CODE;
-    
+
     public final static String NAME = "Mobile Network Code";
-    
+
     /**
      * Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
      * <code>null</code> is returned.
@@ -54,7 +55,7 @@ public final class NetworkCodeQualifier extends ResourceQualifier {
                 // looks like the string we extracted wasn't a valid number.
                 return null;
             }
-            
+
             NetworkCodeQualifier qualifier = new NetworkCodeQualifier();
             qualifier.mCode = code;
             return qualifier;
@@ -72,29 +73,29 @@ public final class NetworkCodeQualifier extends ResourceQualifier {
         if (code != DEFAULT_CODE && code >= 1 && code <= 999) { // code is 1-3 digit.
             return String.format("mnc%1$d", code); //$NON-NLS-1$
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 
     public int getCode() {
         return mCode;
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return "Network Code";
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("mnc"); //$NON-NLS-1$
     }
-    
+
     @Override
     public boolean isValid() {
         return mCode != DEFAULT_CODE;
@@ -113,22 +114,22 @@ public final class NetworkCodeQualifier extends ResourceQualifier {
                 // looks like the string we extracted wasn't a valid number.
                 return false;
             }
-            
+
             NetworkCodeQualifier qualifier = new NetworkCodeQualifier();
             qualifier.mCode = code;
             config.setNetworkCodeQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof NetworkCodeQualifier) {
             return mCode == ((NetworkCodeQualifier)qualifier).mCode;
         }
-        
+
         return false;
     }
 
@@ -136,12 +137,12 @@ public final class NetworkCodeQualifier extends ResourceQualifier {
     public int hashCode() {
         return mCode;
     }
-    
+
     /**
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         return getFolderSegment(mCode);
     }
 
@@ -150,7 +151,7 @@ public final class NetworkCodeQualifier extends ResourceQualifier {
         if (mCode != DEFAULT_CODE) {
             return String.format("MNC %1$d", mCode);
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
index a2c690b..f75e9cb 100644 (file)
@@ -17,6 +17,8 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -27,118 +29,202 @@ import java.util.regex.Pattern;
  * Resource Qualifier for Screen Pixel Density.
  */
 public final class PixelDensityQualifier extends ResourceQualifier {
-    /** Default pixel density value. This means the property is not set. */
-    private final static int DEFAULT_DENSITY = -1;
-
-    private final static Pattern sPixelDensityPattern = Pattern.compile("^(\\d+)dpi$");//$NON-NLS-1$
+    private final static Pattern sDensityLegacyPattern = Pattern.compile("^(\\d+)dpi$");//$NON-NLS-1$
 
     public static final String NAME = "Pixel Density";
 
-    private int mValue = DEFAULT_DENSITY;
-    
+    private Density mValue = Density.MEDIUM;
+
     /**
-     * Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
-     * <code>null</code> is returned.
-     * @param folderSegment the folder segment from which to create a qualifier.
-     * @return a new {@link CountryCodeQualifier} object or <code>null</code>
+     * Screen Orientation enum.
      */
-    public static PixelDensityQualifier getQualifier(String folderSegment) {
-        Matcher m = sPixelDensityPattern.matcher(folderSegment);
-        if (m.matches()) {
-            String v = m.group(1);
-
-            int density = -1;
-            try {
-                density = Integer.parseInt(v);
-            } catch (NumberFormatException e) {
-                // looks like the string we extracted wasn't a valid number.
-                return null;
+    public static enum Density {
+        HIGH("hdpi", 240, "High Density"), //$NON-NLS-1$
+        MEDIUM("mdpi", 160, "Medium Density"), //$NON-NLS-1$
+        LOW("ldpi", 120, "Low Density"), //$NON-NLS-1$
+        NODPI("nodpi", -1, "No Density"); //$NON-NLS-1$
+
+        private final String mValue;
+        private final String mDisplayValue;
+        private final int mDpiValue;
+
+        private Density(String value, int dpiValue, String displayValue) {
+            mValue = value;
+            mDpiValue = dpiValue;
+            mDisplayValue = displayValue;
+        }
+
+        /**
+         * Returns the enum for matching the provided qualifier value.
+         * @param value The qualifier value.
+         * @return the enum for the qualifier value or null if no matching was found.
+         */
+        static Density getEnum(String value) {
+            for (Density orient : values()) {
+                if (orient.mValue.equals(value)) {
+                    return orient;
+                }
             }
-            
-            PixelDensityQualifier qualifier = new PixelDensityQualifier();
-            qualifier.mValue = density;
-            
-            return qualifier;
+
+            return null;
         }
-        return null;
-    }
 
-    /**
-     * Returns the folder name segment for the given value. This is equivalent to calling
-     * {@link #toString()} on a {@link NetworkCodeQualifier} object.
-     * @param value the value of the qualifier, as returned by {@link #getValue()}.
-     */
-    public static String getFolderSegment(int value) {
-        if (value != DEFAULT_DENSITY) {
-            return String.format("%1$ddpi", value); //$NON-NLS-1$
+        static Density getLegacyEnum(String value) {
+            Matcher m = sDensityLegacyPattern.matcher(value);
+            if (m.matches()) {
+                String v = m.group(1);
+
+                try {
+                    int density = Integer.parseInt(v);
+                    for (Density orient : values()) {
+                        if (orient.mDpiValue == density) {
+                            return orient;
+                        }
+                    }
+                } catch (NumberFormatException e) {
+                    // looks like the string we extracted wasn't a valid number
+                    // which really shouldn't happen since the regexp would have failed.
+                }
+            }
+            return null;
         }
-        
-        return ""; //$NON-NLS-1$
+
+        public String getValue() {
+            return mValue;
+        }
+
+        public int getDpiValue() {
+            return mDpiValue;
+        }
+
+        public String getLegacyValue() {
+            if (this != NODPI) {
+                return String.format("%1$ddpi", mDpiValue);
+            }
+
+            return "";
+        }
+
+        public String getDisplayValue() {
+            return mDisplayValue;
+        }
+
+        public static int getIndex(Density value) {
+            int i = 0;
+            for (Density input : values()) {
+                if (value == input) {
+                    return i;
+                }
+
+                i++;
+            }
+
+            return -1;
+        }
+
+        public static Density getByIndex(int index) {
+            int i = 0;
+            for (Density value : values()) {
+                if (i == index) {
+                    return value;
+                }
+                i++;
+            }
+            return null;
+        }
+    }
+
+    public PixelDensityQualifier() {
+        // pass
+    }
+
+    public PixelDensityQualifier(Density value) {
+        mValue = value;
     }
 
-    public int getValue() {
+    public Density getValue() {
         return mValue;
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return NAME;
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("dpi"); //$NON-NLS-1$
     }
-    
+
     @Override
     public boolean isValid() {
-        return mValue != DEFAULT_DENSITY;
+        return mValue != null;
     }
 
     @Override
     public boolean checkAndSet(String value, FolderConfiguration config) {
-        PixelDensityQualifier qualifier = getQualifier(value);
-        if (qualifier != null) {
+        Density density = Density.getEnum(value);
+        if (density == null) {
+            density = Density.getLegacyEnum(value);
+        }
+
+        if (density != null) {
+            PixelDensityQualifier qualifier = new PixelDensityQualifier();
+            qualifier.mValue = density;
             config.setPixelDensityQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof PixelDensityQualifier) {
             return mValue == ((PixelDensityQualifier)qualifier).mValue;
         }
-        
+
         return false;
     }
 
     @Override
     public int hashCode() {
-        return mValue;
+        if (mValue != null) {
+            return mValue.hashCode();
+        }
+
+        return 0;
     }
-    
+
     /**
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
-        return getFolderSegment(mValue);
+    public String getFolderSegment(IAndroidTarget target) {
+        if (mValue != null) {
+            if (target != null) {
+                AndroidVersion version = target.getVersion();
+                if (version.getApiLevel() <= 3 && version.getCodename() == null) {
+                    return mValue.getLegacyValue();
+                }
+            }
+            return mValue.getValue();
+        }
+
+        return ""; //$NON-NLS-1$
     }
 
     @Override
     public String getStringValue() {
-        if (mValue != DEFAULT_DENSITY) {
-            return String.format("%1$d dpi", mValue);
+        if (mValue != null) {
+            return mValue.getDisplayValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
index be54f2b..3f3abcc 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -30,9 +31,9 @@ public final class RegionQualifier extends ResourceQualifier {
     private final static Pattern sRegionPattern = Pattern.compile("^r([A-Z]{2})$"); //$NON-NLS-1$
 
     public static final String NAME = "Region";
-    
+
     private String mValue;
-    
+
     /**
      * Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
      * <code>null</code> is returned.
@@ -49,7 +50,7 @@ public final class RegionQualifier extends ResourceQualifier {
         }
         return null;
     }
-    
+
     /**
      * Returns the folder name segment for the given value. This is equivalent to calling
      * {@link #toString()} on a {@link RegionQualifier} object.
@@ -62,7 +63,7 @@ public final class RegionQualifier extends ResourceQualifier {
                 return segment;
             }
         }
-            
+
         return "";  //$NON-NLS-1$
     }
 
@@ -70,20 +71,20 @@ public final class RegionQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue;
         }
-        
+
         return ""; //$NON-NLS-1$
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return NAME;
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("region"); //$NON-NLS-1$
@@ -101,10 +102,10 @@ public final class RegionQualifier extends ResourceQualifier {
             config.setRegionQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof RegionQualifier) {
@@ -113,7 +114,7 @@ public final class RegionQualifier extends ResourceQualifier {
             }
             return mValue.equals(((RegionQualifier)qualifier).mValue);
         }
-        
+
         return false;
     }
 
@@ -122,15 +123,15 @@ public final class RegionQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.hashCode();
         }
-        
+
         return 0;
     }
-    
+
     /**
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         return getFolderSegment(mValue);
     }
 
@@ -139,7 +140,7 @@ public final class RegionQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue;
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
index b644e8f..bfee8d2 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
+import com.android.sdklib.IAndroidTarget;
+
 import org.eclipse.swt.graphics.Image;
 
 /**
@@ -23,28 +25,28 @@ import org.eclipse.swt.graphics.Image;
  * <p/>The resource qualifier classes are designed as immutable.
  */
 public abstract class ResourceQualifier implements Comparable<ResourceQualifier> {
-    
+
     /**
      * Returns the human readable name of the qualifier.
      */
     public abstract String getName();
-    
+
     /**
      * Returns a shorter human readable name for the qualifier.
      * @see #getName()
      */
     public abstract String getShortName();
-    
+
     /**
      * Returns the icon for the qualifier.
      */
     public abstract Image getIcon();
-    
+
     /**
      * Returns whether the qualifier has a valid filter value.
      */
     public abstract boolean isValid();
-    
+
     /**
      * Check if the value is valid for this qualifier, and if so sets the value
      * into a Folder Configuration.
@@ -53,13 +55,17 @@ public abstract class ResourceQualifier implements Comparable<ResourceQualifier>
      * @return true if the value was valid and was set.
      */
     public abstract boolean checkAndSet(String value, FolderConfiguration config);
-    
+
     /**
      * Returns a string formated to be used in a folder name.
      * <p/>This is declared as abstract to force children classes to implement it.
      */
+    public abstract String getFolderSegment(IAndroidTarget target);
+
     @Override
-    public abstract String toString();
+    public String toString() {
+        return getFolderSegment(null);
+    }
 
     /**
      * Returns a string formatted for display purpose.
@@ -72,7 +78,7 @@ public abstract class ResourceQualifier implements Comparable<ResourceQualifier>
      */
     @Override
     public abstract boolean equals(Object object);
-    
+
     /**
      * Returns a hash code value for the object.
      * <p/>This is declared as abstract to force children classes to implement it.
index e756644..ff8a930 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -46,7 +47,7 @@ public final class ScreenDimensionQualifier extends ResourceQualifier {
      * 200 but that'll be Y in landscape and X in portrait.
      * Default value is <code>DEFAULT_SIZE</code> */
     private int mValue2 = DEFAULT_SIZE;
-    
+
     public int getValue1() {
         return mValue1;
     }
@@ -54,17 +55,17 @@ public final class ScreenDimensionQualifier extends ResourceQualifier {
     public int getValue2() {
         return mValue2;
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return "Dimension";
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("dimension"); //$NON-NLS-1$
@@ -81,7 +82,7 @@ public final class ScreenDimensionQualifier extends ResourceQualifier {
         if (m.matches()) {
             String d1 = m.group(1);
             String d2 = m.group(2);
-            
+
             ScreenDimensionQualifier qualifier = getQualifier(d1, d2);
             if (qualifier != null) {
                 config.setScreenDimensionQualifier(qualifier);
@@ -90,14 +91,14 @@ public final class ScreenDimensionQualifier extends ResourceQualifier {
         }
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof ScreenDimensionQualifier) {
             ScreenDimensionQualifier q = (ScreenDimensionQualifier)qualifier;
             return (mValue1 == q.mValue1 && mValue2 == q.mValue2);
         }
-        
+
         return false;
     }
 
@@ -105,12 +106,12 @@ public final class ScreenDimensionQualifier extends ResourceQualifier {
     public int hashCode() {
         return toString().hashCode();
     }
-    
+
     public static ScreenDimensionQualifier getQualifier(String size1, String size2) {
         try {
             int s1 = Integer.parseInt(size1);
             int s2 = Integer.parseInt(size2);
-            
+
             ScreenDimensionQualifier qualifier = new ScreenDimensionQualifier();
 
             if (s1 > s2) {
@@ -125,7 +126,7 @@ public final class ScreenDimensionQualifier extends ResourceQualifier {
         } catch (NumberFormatException e) {
             // looks like the string we extracted wasn't a valid number.
         }
-        
+
         return null;
     }
 
@@ -133,7 +134,7 @@ public final class ScreenDimensionQualifier extends ResourceQualifier {
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         return String.format("%1$dx%2$d", mValue1, mValue2); //$NON-NLS-1$
     }
 
@@ -142,7 +143,7 @@ public final class ScreenDimensionQualifier extends ResourceQualifier {
         if (mValue1 != -1 && mValue2 != -1) {
             return String.format("%1$dx%2$d", mValue1, mValue2);
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
index 6e1b829..85d0c03 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -24,7 +25,7 @@ import org.eclipse.swt.graphics.Image;
  * Resource Qualifier for Screen Orientation.
  */
 public final class ScreenOrientationQualifier extends ResourceQualifier {
-    
+
     public static final String NAME = "Screen Orientation";
 
     private ScreenOrientation mValue = null;
@@ -36,15 +37,15 @@ public final class ScreenOrientationQualifier extends ResourceQualifier {
         PORTRAIT("port", "Portrait"), //$NON-NLS-1$
         LANDSCAPE("land", "Landscape"), //$NON-NLS-1$
         SQUARE("square", "Square"); //$NON-NLS-1$
-        
+
         private String mValue;
         private String mDisplayValue;
-        
+
         private ScreenOrientation(String value, String displayValue) {
             mValue = value;
             mDisplayValue = displayValue;
         }
-        
+
         /**
          * Returns the enum for matching the provided qualifier value.
          * @param value The qualifier value.
@@ -63,18 +64,18 @@ public final class ScreenOrientationQualifier extends ResourceQualifier {
         public String getValue() {
             return mValue;
         }
-        
+
         public String getDisplayValue() {
             return mDisplayValue;
         }
-        
+
         public static int getIndex(ScreenOrientation orientation) {
             int i = 0;
             for (ScreenOrientation orient : values()) {
                 if (orient == orientation) {
                     return i;
                 }
-                
+
                 i++;
             }
 
@@ -104,22 +105,22 @@ public final class ScreenOrientationQualifier extends ResourceQualifier {
     public ScreenOrientation getValue() {
         return mValue;
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return "Orientation";
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("orientation"); //$NON-NLS-1$
     }
-    
+
     @Override
     public boolean isValid() {
         return mValue != null;
@@ -133,10 +134,10 @@ public final class ScreenOrientationQualifier extends ResourceQualifier {
             config.setScreenOrientationQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof ScreenOrientationQualifier) {
@@ -151,19 +152,19 @@ public final class ScreenOrientationQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.hashCode();
         }
-        
+
         return 0;
     }
-    
+
     /**
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         if (mValue != null) {
             return mValue.getValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 
@@ -172,7 +173,7 @@ public final class ScreenOrientationQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.getDisplayValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenRatioQualifier.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenRatioQualifier.java
new file mode 100644 (file)
index 0000000..55857fd
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2009 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.resources.configurations;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.swt.graphics.Image;
+
+public class ScreenRatioQualifier extends ResourceQualifier {
+
+    public static final String NAME = "Screen Ratio";
+
+    private ScreenRatio mValue = null;
+
+    /**
+     * Screen Orientation enum.
+     */
+    public static enum ScreenRatio {
+        NOTLONG("notlong", "Not Long"), //$NON-NLS-1$
+        LONG("long", "Long"); //$NON-NLS-1$
+
+        private String mValue;
+        private String mDisplayValue;
+
+        private ScreenRatio(String value, String displayValue) {
+            mValue = value;
+            mDisplayValue = displayValue;
+        }
+
+        /**
+         * Returns the enum for matching the provided qualifier value.
+         * @param value The qualifier value.
+         * @return the enum for the qualifier value or null if no matching was found.
+         */
+        static ScreenRatio getEnum(String value) {
+            for (ScreenRatio orient : values()) {
+                if (orient.mValue.equals(value)) {
+                    return orient;
+                }
+            }
+
+            return null;
+        }
+
+        public String getValue() {
+            return mValue;
+        }
+
+        public String getDisplayValue() {
+            return mDisplayValue;
+        }
+
+        public static int getIndex(ScreenRatio orientation) {
+            int i = 0;
+            for (ScreenRatio orient : values()) {
+                if (orient == orientation) {
+                    return i;
+                }
+
+                i++;
+            }
+
+            return -1;
+        }
+
+        public static ScreenRatio getByIndex(int index) {
+            int i = 0;
+            for (ScreenRatio orient : values()) {
+                if (i == index) {
+                    return orient;
+                }
+                i++;
+            }
+
+            return null;
+        }
+    }
+
+    public ScreenRatioQualifier() {
+    }
+
+    public ScreenRatioQualifier(ScreenRatio value) {
+        mValue = value;
+    }
+
+    public ScreenRatio getValue() {
+        return mValue;
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public String getShortName() {
+        return "Ratio";
+    }
+
+    @Override
+    public Image getIcon() {
+        return IconFactory.getInstance().getIcon("ratio"); //$NON-NLS-1$
+    }
+
+    @Override
+    public boolean isValid() {
+        return mValue != null;
+    }
+
+    @Override
+    public boolean checkAndSet(String value, FolderConfiguration config) {
+        ScreenRatio size = ScreenRatio.getEnum(value);
+        if (size != null) {
+            ScreenRatioQualifier qualifier = new ScreenRatioQualifier(size);
+            config.setScreenRatioQualifier(qualifier);
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object qualifier) {
+        if (qualifier instanceof ScreenRatioQualifier) {
+            return mValue == ((ScreenRatioQualifier)qualifier).mValue;
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        if (mValue != null) {
+            return mValue.hashCode();
+        }
+
+        return 0;
+    }
+
+    /**
+     * Returns the string used to represent this qualifier in the folder name.
+     */
+    @Override
+    public String getFolderSegment(IAndroidTarget target) {
+        if (mValue != null) {
+            if (target == null) {
+                // Default behavior (when target==null) is qualifier is supported
+                return mValue.getValue();
+            }
+
+            AndroidVersion version = target.getVersion();
+            if (version.getApiLevel() >= 4 ||
+                    (version.getApiLevel() == 3 && "Donut".equals(version.getCodename()))) {
+                return mValue.getValue();
+            }
+        }
+
+        return ""; //$NON-NLS-1$
+    }
+
+    @Override
+    public String getStringValue() {
+        if (mValue != null) {
+            return mValue.getDisplayValue();
+        }
+
+        return ""; //$NON-NLS-1$
+    }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenSizeQualifier.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenSizeQualifier.java
new file mode 100644 (file)
index 0000000..d148efc
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2009 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.resources.configurations;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Resource Qualifier for Screen Size. Size can be "small", "normal", and "large"
+ */
+public class ScreenSizeQualifier extends ResourceQualifier {
+
+    public static final String NAME = "Screen Size";
+
+    private ScreenSize mValue = null;
+
+    /**
+     * Screen Orientation enum.
+     */
+    public static enum ScreenSize {
+        SMALL("small", "Small"), //$NON-NLS-1$
+        NORMAL("normal", "Normal"), //$NON-NLS-1$
+        LARGE("large", "Large"); //$NON-NLS-1$
+
+        private String mValue;
+        private String mDisplayValue;
+
+        private ScreenSize(String value, String displayValue) {
+            mValue = value;
+            mDisplayValue = displayValue;
+        }
+
+        /**
+         * Returns the enum for matching the provided qualifier value.
+         * @param value The qualifier value.
+         * @return the enum for the qualifier value or null if no matching was found.
+         */
+        static ScreenSize getEnum(String value) {
+            for (ScreenSize orient : values()) {
+                if (orient.mValue.equals(value)) {
+                    return orient;
+                }
+            }
+
+            return null;
+        }
+
+        public String getValue() {
+            return mValue;
+        }
+
+        public String getDisplayValue() {
+            return mDisplayValue;
+        }
+
+        public static int getIndex(ScreenSize orientation) {
+            int i = 0;
+            for (ScreenSize orient : values()) {
+                if (orient == orientation) {
+                    return i;
+                }
+
+                i++;
+            }
+
+            return -1;
+        }
+
+        public static ScreenSize getByIndex(int index) {
+            int i = 0;
+            for (ScreenSize orient : values()) {
+                if (i == index) {
+                    return orient;
+                }
+                i++;
+            }
+
+            return null;
+        }
+    }
+
+    public ScreenSizeQualifier() {
+    }
+
+    public ScreenSizeQualifier(ScreenSize value) {
+        mValue = value;
+    }
+
+    public ScreenSize getValue() {
+        return mValue;
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public String getShortName() {
+        return "Size";
+    }
+
+    @Override
+    public Image getIcon() {
+        return IconFactory.getInstance().getIcon("size"); //$NON-NLS-1$
+    }
+
+    @Override
+    public boolean isValid() {
+        return mValue != null;
+    }
+
+    @Override
+    public boolean checkAndSet(String value, FolderConfiguration config) {
+        ScreenSize size = ScreenSize.getEnum(value);
+        if (size != null) {
+            ScreenSizeQualifier qualifier = new ScreenSizeQualifier(size);
+            config.setScreenSizeQualifier(qualifier);
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object qualifier) {
+        if (qualifier instanceof ScreenSizeQualifier) {
+            return mValue == ((ScreenSizeQualifier)qualifier).mValue;
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        if (mValue != null) {
+            return mValue.hashCode();
+        }
+
+        return 0;
+    }
+
+    /**
+     * Returns the string used to represent this qualifier in the folder name.
+     */
+    @Override
+    public String getFolderSegment(IAndroidTarget target) {
+        if (mValue != null) {
+            if (target == null) {
+                // Default behavior (when target==null) is qualifier is supported
+                return mValue.getValue();
+            }
+
+            AndroidVersion version = target.getVersion();
+            if (version.getApiLevel() >= 4 ||
+                    (version.getApiLevel() == 3 && "Donut".equals(version.getCodename()))) {
+                return mValue.getValue();
+            }
+        }
+
+        return ""; //$NON-NLS-1$
+    }
+
+    @Override
+    public String getStringValue() {
+        if (mValue != null) {
+            return mValue.getDisplayValue();
+        }
+
+        return ""; //$NON-NLS-1$
+    }
+}
index add45cc..6289147 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -31,7 +32,7 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
     public static final String NAME = "Text Input Method";
 
     private TextInputMethod mValue;
-    
+
     /**
      * Screen Orientation enum.
      */
@@ -39,15 +40,15 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
         NOKEY("nokeys", "No Keys"), //$NON-NLS-1$
         QWERTY("qwerty", "Qwerty"), //$NON-NLS-1$
         TWELVEKEYS("12key", "12 Key"); //$NON-NLS-1$
-        
+
         private String mValue;
         private String mDisplayValue;
-        
+
         private TextInputMethod(String value, String displayValue) {
             mValue = value;
             mDisplayValue = displayValue;
         }
-        
+
         /**
          * Returns the enum for matching the provided qualifier value.
          * @param value The qualifier value.
@@ -59,14 +60,14 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
                     return orient;
                 }
             }
-            
+
             return null;
         }
 
         public String getValue() {
             return mValue;
         }
-        
+
         public String getDisplayValue() {
             return mDisplayValue;
         }
@@ -77,7 +78,7 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
                 if (value == input) {
                     return i;
                 }
-                
+
                 i++;
             }
 
@@ -95,7 +96,7 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
             return null;
         }
     }
-    
+
     public TextInputMethodQualifier() {
         // pass
     }
@@ -107,17 +108,17 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
     public TextInputMethod getValue() {
         return mValue;
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return "Text Input";
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("text_input"); //$NON-NLS-1$
@@ -137,10 +138,10 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
             config.setTextInputMethodQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof TextInputMethodQualifier) {
@@ -155,7 +156,7 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.hashCode();
         }
-        
+
         return 0;
     }
 
@@ -163,11 +164,11 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         if (mValue != null) {
             return mValue.getValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 
@@ -176,7 +177,7 @@ public final class TextInputMethodQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.getDisplayValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
index 1ed7d95..758c87f 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.resources.configurations;
 
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.swt.graphics.Image;
 
@@ -29,7 +30,7 @@ public final class TouchScreenQualifier extends ResourceQualifier {
     public static final String NAME = "Touch Screen";
 
     private TouchScreenType mValue;
-    
+
     /**
      * Screen Orientation enum.
      */
@@ -37,15 +38,15 @@ public final class TouchScreenQualifier extends ResourceQualifier {
         NOTOUCH("notouch", "No Touch"), //$NON-NLS-1$
         STYLUS("stylus", "Stylus"), //$NON-NLS-1$
         FINGER("finger", "Finger"); //$NON-NLS-1$
-        
+
         private String mValue;
         private String mDisplayValue;
-        
+
         private TouchScreenType(String value, String displayValue) {
             mValue = value;
             mDisplayValue = displayValue;
         }
-        
+
         /**
          * Returns the enum for matching the provided qualifier value.
          * @param value The qualifier value.
@@ -57,14 +58,14 @@ public final class TouchScreenQualifier extends ResourceQualifier {
                     return orient;
                 }
             }
-            
+
             return null;
         }
 
         public String getValue() {
             return mValue;
         }
-        
+
         public String getDisplayValue() {
             return mDisplayValue;
         }
@@ -75,7 +76,7 @@ public final class TouchScreenQualifier extends ResourceQualifier {
                 if (t == touch) {
                     return i;
                 }
-                
+
                 i++;
             }
 
@@ -94,7 +95,7 @@ public final class TouchScreenQualifier extends ResourceQualifier {
             return null;
         }
     }
-    
+
     public TouchScreenQualifier() {
         // pass
     }
@@ -106,17 +107,17 @@ public final class TouchScreenQualifier extends ResourceQualifier {
     public TouchScreenType getValue() {
         return mValue;
     }
-    
+
     @Override
     public String getName() {
         return NAME;
     }
-    
+
     @Override
     public String getShortName() {
         return NAME;
     }
-    
+
     @Override
     public Image getIcon() {
         return IconFactory.getInstance().getIcon("touch"); //$NON-NLS-1$
@@ -136,10 +137,10 @@ public final class TouchScreenQualifier extends ResourceQualifier {
             config.setTouchTypeQualifier(qualifier);
             return true;
         }
-        
+
         return false;
     }
-    
+
     @Override
     public boolean equals(Object qualifier) {
         if (qualifier instanceof TouchScreenQualifier) {
@@ -153,7 +154,7 @@ public final class TouchScreenQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.hashCode();
         }
-        
+
         return 0;
     }
 
@@ -161,11 +162,11 @@ public final class TouchScreenQualifier extends ResourceQualifier {
      * Returns the string used to represent this qualifier in the folder name.
      */
     @Override
-    public String toString() {
+    public String getFolderSegment(IAndroidTarget target) {
         if (mValue != null) {
             return mValue.getValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 
@@ -174,7 +175,7 @@ public final class TouchScreenQualifier extends ResourceQualifier {
         if (mValue != null) {
             return mValue.getDisplayValue();
         }
-        
+
         return ""; //$NON-NLS-1$
     }
 }
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/VersionQualifier.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/VersionQualifier.java
new file mode 100644 (file)
index 0000000..5744fea
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2009 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.resources.configurations;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.swt.graphics.Image;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Resource Qualifier for Platform Version.
+ */
+public final class VersionQualifier extends ResourceQualifier {
+    /** Default pixel density value. This means the property is not set. */
+    private final static int DEFAULT_VERSION = -1;
+
+    private final static Pattern sCountryCodePattern = Pattern.compile("^v(\\d+)$");//$NON-NLS-1$
+
+    private int mVersion = DEFAULT_VERSION;
+
+    public static final String NAME = "Platform Version";
+
+    /**
+     * Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
+     * <code>null</code> is returned.
+     * @param segment the folder segment from which to create a qualifier.
+     * @return a new {@link VersionQualifier} object or <code>null</code>
+     */
+    public static VersionQualifier getQualifier(String segment) {
+        Matcher m = sCountryCodePattern.matcher(segment);
+        if (m.matches()) {
+            String v = m.group(1);
+
+            int code = -1;
+            try {
+                code = Integer.parseInt(v);
+            } catch (NumberFormatException e) {
+                // looks like the string we extracted wasn't a valid number.
+                return null;
+            }
+
+            VersionQualifier qualifier = new VersionQualifier();
+            qualifier.mVersion = code;
+            return qualifier;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the folder name segment for the given value. This is equivalent to calling
+     * {@link #toString()} on a {@link VersionQualifier} object.
+     * @param version the value of the qualifier, as returned by {@link #getVersion()}.
+     */
+    public static String getFolderSegment(int version) {
+        if (version != DEFAULT_VERSION) {
+            return String.format("v%1$d", version); //$NON-NLS-1$
+        }
+
+        return ""; //$NON-NLS-1$
+    }
+
+    public int getVersion() {
+        return mVersion;
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public String getShortName() {
+        return "Version";
+    }
+
+    @Override
+    public Image getIcon() {
+        return IconFactory.getInstance().getIcon("version"); //$NON-NLS-1$
+    }
+
+    @Override
+    public boolean isValid() {
+        return mVersion != DEFAULT_VERSION;
+    }
+
+    @Override
+    public boolean checkAndSet(String value, FolderConfiguration config) {
+        VersionQualifier qualifier = getQualifier(value);
+        if (qualifier != null) {
+            config.setVersionQualifier(qualifier);
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object qualifier) {
+        if (qualifier instanceof VersionQualifier) {
+            return mVersion == ((VersionQualifier)qualifier).mVersion;
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mVersion;
+    }
+
+    /**
+     * Returns the string used to represent this qualifier in the folder name.
+     */
+    @Override
+    public String getFolderSegment(IAndroidTarget target) {
+        if (target == null) {
+            // Default behavior (when target==null) is qualifier is supported
+            return getFolderSegment(mVersion);
+        }
+
+        AndroidVersion version = target.getVersion();
+        if (version.getApiLevel() >= 3) {
+            return getFolderSegment(mVersion);
+        }
+
+        return ""; //$NON-NLS-1$
+    }
+
+    @Override
+    public String getStringValue() {
+        if (mVersion != DEFAULT_VERSION) {
+            return String.format("API %1$d", mVersion);
+        }
+
+        return ""; //$NON-NLS-1$
+    }
+}
index 3cb06fd..88693b0 100644 (file)
@@ -52,13 +52,13 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
 
     /** List of the qualifier object helping for the parsing of folder names */
     private final ResourceQualifier[] mQualifiers;
-    
+
     /**
      * Map associating project resource with project objects.
      */
     private final HashMap<IProject, ProjectResources> mMap =
         new HashMap<IProject, ProjectResources>();
-    
+
     /**
      * Sets up the resource manager with the global resource monitor.
      * @param monitor The global resource monitor
@@ -68,10 +68,10 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
         int mask = IResourceDelta.ADDED | IResourceDelta.REMOVED | IResourceDelta.CHANGED;
         monitor.addFolderListener(sThis, mask);
         monitor.addFileListener(sThis, mask);
-        
+
         CompiledResourcesMonitor.setupMonitor(monitor);
     }
-    
+
     /**
      * Returns the singleton instance.
      */
@@ -87,15 +87,15 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
     public ProjectResources getProjectResources(IProject project) {
         return mMap.get(project);
     }
-    
+
     /**
      * Processes folder event.
      */
     public void folderChanged(IFolder folder, int kind) {
         ProjectResources resources;
-        
+
         final IProject project = folder.getProject();
-        
+
         try {
             if (project.hasNature(AndroidConstants.NATURE) == false) {
                 return;
@@ -104,18 +104,18 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
             // can't get the project nature? return!
             return;
         }
-        
+
         switch (kind) {
             case IResourceDelta.ADDED:
                 // checks if the folder is under res.
                 IPath path = folder.getFullPath();
-                
+
                 // the path will be project/res/<something>
                 if (path.segmentCount() == 3) {
                     if (isInResFolder(path)) {
                         // get the project and its resource object.
                         resources = mMap.get(project);
-                        
+
                         // if it doesn't exist, we create it.
                         if (resources == null) {
                             resources = new ProjectResources(false /* isFrameworkRepository */);
@@ -146,23 +146,23 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
                 break;
         }
     }
-    
+
     /* (non-Javadoc)
      * Sent when a file changed. Depending on the file being changed, and the type of change (ADDED,
      * REMOVED, CHANGED), the file change is processed to update the resource manager data.
-     * 
+     *
      * @param file The file that changed.
      * @param markerDeltas The marker deltas for the file.
      * @param kind The change kind. This is equivalent to
      * {@link IResourceDelta#accept(IResourceDeltaVisitor)}
-     * 
+     *
      * @see IFileListener#fileChanged
      */
     public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
         ProjectResources resources;
-        
+
         final IProject project = file.getProject();
-        
+
         try {
             if (project.hasNature(AndroidConstants.NATURE) == false) {
                 return;
@@ -171,22 +171,22 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
             // can't get the project nature? return!
             return;
         }
-        
+
         switch (kind) {
             case IResourceDelta.ADDED:
                 // checks if the file is under res/something.
                 IPath path = file.getFullPath();
-                
+
                 if (path.segmentCount() == 4) {
                     if (isInResFolder(path)) {
                         // get the project and its resources
                         resources = mMap.get(project);
-        
+
                         IContainer container = file.getParent();
                         if (container instanceof IFolder && resources != null) {
-                            
+
                             ResourceFolder folder = resources.getResourceFolder((IFolder)container);
-                            
+
                             if (folder != null) {
                                 processFile(new IFileWrapper(file), folder);
                             }
@@ -201,7 +201,7 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
                     IContainer container = file.getParent();
                     if (container instanceof IFolder) {
                         ResourceFolder resFolder = resources.getResourceFolder((IFolder)container);
-                        
+
                         // we get the delete on the folder before the file, so it is possible
                         // the associated ResourceFolder doesn't exist anymore.
                         if (resFolder != null) {
@@ -221,7 +221,7 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
                     IContainer container = file.getParent();
                     if (container instanceof IFolder) {
                         ResourceFolder resFolder = resources.getResourceFolder((IFolder)container);
-                        
+
                         // we get the delete on the folder before the file, so it is possible
                         // the associated ResourceFolder doesn't exist anymore.
                         if (resFolder != null) {
@@ -249,7 +249,7 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
     public void projectOpenedWithWorkspace(IProject project) {
         createProject(project);
     }
-    
+
     /**
      * Returns the {@link ResourceFolder} for the given file or <code>null</code> if none exists.
      */
@@ -258,62 +258,85 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
         if (container.getType() == IResource.FOLDER) {
             IFolder parent = (IFolder)container;
             IProject project = file.getProject();
-            
+
             ProjectResources resources = getProjectResources(project);
             if (resources != null) {
                 return resources.getResourceFolder(parent);
             }
         }
-        
+
         return null;
     }
-    
+
     /**
      * Loads and returns the resources for a given {@link IAndroidTarget}
      * @param androidTarget the target from which to load the framework resources
      */
     public ProjectResources loadFrameworkResources(IAndroidTarget androidTarget) {
         String osResourcesPath = androidTarget.getPath(IAndroidTarget.RESOURCES);
-        
+
         File frameworkRes = new File(osResourcesPath);
         if (frameworkRes.isDirectory()) {
             ProjectResources resources = new ProjectResources(true /* isFrameworkRepository */);
 
             try {
-                File[] files = frameworkRes.listFiles();
-                for (File file : files) {
-                    if (file.isDirectory()) {
-                        ResourceFolder resFolder = processFolder(new FolderWrapper(file),
-                                resources);
-                        
-                        if (resFolder != null) {
-                            // now we process the content of the folder
-                            File[] children = file.listFiles();
-                            
-                            for (File childRes : children) {
-                                if (childRes.isFile()) {
-                                    processFile(new FileWrapper(childRes), resFolder);
-                                }
-                            }
-                        }
-                        
-                    }
-                }
-                
-                // now that we have loaded the files, we need to force load the resources from them
-                resources.loadAll();
-                
+                loadResources(resources, frameworkRes);
                 return resources;
-                
             } catch (IOException e) {
                 // since we test that folders are folders, and files are files, this shouldn't
                 // happen. We can ignore it.
             }
         }
-        
+
         return null;
     }
-    
+
+    /**
+     * Loads the resources from a folder, and fills the given {@link ProjectResources}.
+     * <p/>
+     * This is mostly a utility method that should not be used to process actual Eclipse projects
+     * (Those are loaded with {@link #createProject(IProject)} for new project or
+     * {@link #processFolder(IAbstractFolder, ProjectResources)} and
+     * {@link #processFile(IAbstractFile, ResourceFolder)} for folder/file modifications)<br>
+     * This method will process files/folders with implementations of {@link IAbstractFile} and
+     * {@link IAbstractFolder} based on {@link File} instead of {@link IFile} and {@link IFolder}
+     * respectively. This is not proper for handling {@link IProject}s.
+     * </p>
+     * This is used to load the framework resources, or to do load project resources when
+     * setting rendering tests.
+     *
+     *
+     * @param resources The {@link ProjectResources} files to load. It is expected that the
+     * framework flag has been properly setup. This is filled up with the content of the folder.
+     * @param folder The folder to read the resources from. This is the top level resource folder
+     * (res/)
+     * @throws IOException
+     */
+    public void loadResources(ProjectResources resources, File folder) throws IOException {
+        File[] files = folder.listFiles();
+        for (File file : files) {
+            if (file.isDirectory()) {
+                ResourceFolder resFolder = processFolder(new FolderWrapper(file),
+                        resources);
+
+                if (resFolder != null) {
+                    // now we process the content of the folder
+                    File[] children = file.listFiles();
+
+                    for (File childRes : children) {
+                        if (childRes.isFile()) {
+                            processFile(new FileWrapper(childRes), resFolder);
+                        }
+                    }
+                }
+
+            }
+        }
+
+        // now that we have loaded the files, we need to force load the resources from them
+        resources.loadAll();
+    }
+
     /**
      * Initial project parsing to gather resource info.
      * @param project
@@ -328,33 +351,33 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
                 // can't check the nature of the project? ignore it.
                 return;
             }
-            
+
             IFolder resourceFolder = project.getFolder(SdkConstants.FD_RESOURCES);
-            
+
             ProjectResources projectResources = mMap.get(project);
             if (projectResources == null) {
                 projectResources = new ProjectResources(false /* isFrameworkRepository */);
                 mMap.put(project, projectResources);
             }
-            
+
             if (resourceFolder != null && resourceFolder.exists()) {
                 try {
                     IResource[] resources = resourceFolder.members();
-                    
+
                     for (IResource res : resources) {
                         if (res.getType() == IResource.FOLDER) {
                             IFolder folder = (IFolder)res;
                             ResourceFolder resFolder = processFolder(new IFolderWrapper(folder),
                                     projectResources);
-                            
+
                             if (resFolder != null) {
                                 // now we process the content of the folder
                                 IResource[] files = folder.members();
-                                
+
                                 for (IResource fileRes : files) {
                                     if (fileRes.getType() == IResource.FILE) {
                                         IFile file = (IFile)fileRes;
-                                        
+
                                         processFile(new IFileWrapper(file), resFolder);
                                     }
                                 }
@@ -383,10 +406,10 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
         // Because the order of the qualifier is fixed, we do not reset the first qualifier
         // after each sucessful segment.
         // If we run out of qualifier before processing all the segments, we fail.
-        
+
         int qualifierIndex = 0;
         int qualifierCount = mQualifiers.length;
-        
+
         for (int i = 1 ; i < folderSegments.length; i++) {
             String seg = folderSegments[i];
             if (seg.length() > 0) {
@@ -394,12 +417,12 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
                         mQualifiers[qualifierIndex].checkAndSet(seg, config) == false) {
                     qualifierIndex++;
                 }
-                
+
                 // if we reached the end of the qualifier we didn't find a matching qualifier.
                 if (qualifierIndex == qualifierCount) {
                     return null;
                 }
-                
+
             } else {
                 return null;
             }
@@ -407,7 +430,7 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
 
         return config;
     }
-    
+
     /**
      * Processes a folder and adds it to the list of the project resources.
      * @param folder the folder to process
@@ -420,18 +443,18 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
 
         // get the enum for the resource type.
         ResourceFolderType type = ResourceFolderType.getTypeByName(folderSegments[0]);
-        
+
         if (type != null) {
             // get the folder configuration.
             FolderConfiguration config = getConfig(folderSegments);
-            
+
             if (config != null) {
                 ResourceFolder configuredFolder = project.add(type, config, folder);
 
                 return configuredFolder;
             }
         }
-        
+
         return null;
     }
 
@@ -443,16 +466,16 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
     private void processFile(IAbstractFile file, ResourceFolder folder) {
         // get the type of the folder
         ResourceFolderType type = folder.getType();
-        
+
         // look for this file if it's already been created
         ResourceFile resFile = folder.getFile(file);
-        
+
         if (resFile != null) {
             // invalidate the file
             resFile.touch();
         } else {
             // create a ResourceFile for it.
-            
+
             // check if that's a single or multi resource type folder. For now we define this by
             // the number of possible resource type output by files in the folder. This does
             // not make the difference between several resource types from a single file or
@@ -460,13 +483,13 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
             // resource. The former is handled by MultiResourceFile properly while we don't
             // handle the latter. If we were to add this behavior we'd have to change this call.
             ResourceType[] types = FolderTypeRelationship.getRelatedResourceTypes(type);
-    
+
             if (types.length == 1) {
                 resFile = new SingleResourceFile(file, folder);
             } else {
                 resFile = new MultiResourceFile(file, folder);
             }
-    
+
             // add it to the folder
             folder.addFile(resFile);
         }
@@ -480,7 +503,7 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
     private boolean isInResFolder(IPath path) {
         return SdkConstants.FD_RESOURCES.equalsIgnoreCase(path.segment(1));
     }
-    
+
     /**
      * Private constructor to enforce singleton design.
      */
index d6cfeae..f2e883d 100644 (file)
@@ -378,6 +378,20 @@ public class Sdk implements IProjectListener {
     }
 
     /**
+     * Return the {@link AndroidTargetData} for a given {@link IProject}.
+     */
+    public AndroidTargetData getTargetData(IProject project) {
+        synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
+            IAndroidTarget target = getTarget(project);
+            if (target != null) {
+                return getTargetData(target);
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * Returns the configuration map for a given project.
      * <p/>The Map key are name to be used in the apk filename, while the values are comma separated
      * config values. The config value can be passed directly to aapt through the -c option.
index 7378d41..358b6fd 100644 (file)
@@ -27,11 +27,17 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQuali
 import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier;
 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenRatioQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier;
 import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier;
 import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.VersionQualifier;
 import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier.KeyboardState;
 import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier.NavigationMethod;
+import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density;
 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenRatioQualifier.ScreenRatio;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier.ScreenSize;
 import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier.TextInputMethod;
 import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier.TouchScreenType;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
@@ -80,13 +86,13 @@ import java.util.HashMap;
  * <ul>
  * <li>Use {@link #setConfiguration(String)} or {@link #setConfiguration(FolderConfiguration)}.
  * <li>Retrieve the configuration using {@link #getConfiguration(FolderConfiguration)}.
- * </ul> 
+ * </ul>
  */
 public class ConfigurationSelector extends Composite {
-    
+
     public static final int WIDTH_HINT = 600;
     public static final int HEIGHT_HINT = 250;
-    
+
     private Runnable mOnChangeListener;
 
     private TableViewer mFullTableViewer;
@@ -94,16 +100,16 @@ public class ConfigurationSelector extends Composite {
     private Button mAddButton;
     private Button mRemoveButton;
     private StackLayout mStackLayout;
-    
+
     private boolean mOnRefresh = false;
 
     private final FolderConfiguration mBaseConfiguration = new FolderConfiguration();
     private final FolderConfiguration mSelectedConfiguration = new FolderConfiguration();
-    
+
     private final HashMap<Class<? extends ResourceQualifier>, QualifierEditBase> mUiMap =
         new HashMap<Class<? extends ResourceQualifier>, QualifierEditBase>();
     private Composite mQualifierEditParent;
-    
+
     /**
      * Basic of {@link VerifyListener} to only accept digits.
      */
@@ -119,7 +125,7 @@ public class ConfigurationSelector extends Composite {
             }
         }
     }
-    
+
     /**
      * Implementation of {@link VerifyListener} for Country Code qualifiers.
      */
@@ -138,7 +144,7 @@ public class ConfigurationSelector extends Composite {
             }
         }
     }
-    
+
     /**
      * Implementation of {@link VerifyListener} for the Language and Region qualifiers.
      */
@@ -149,7 +155,7 @@ public class ConfigurationSelector extends Composite {
                 e.doit = false;
                 return;
             }
-            
+
             // check for lower case only.
             for (int i = 0 ; i < e.text.length(); i++) {
                 char letter = e.text.charAt(i);
@@ -160,17 +166,17 @@ public class ConfigurationSelector extends Composite {
             }
         }
     }
-    
+
     /**
      * Implementation of {@link VerifyListener} for the Pixel Density qualifier.
      */
     public static class DensityVerifier extends DigitVerifier { }
-    
+
     /**
      * Implementation of {@link VerifyListener} for the Screen Dimension qualifier.
      */
     public static class DimensionVerifier extends DigitVerifier { }
-    
+
     /**
      * Enum for the state of the configuration being created.
      */
@@ -186,18 +192,18 @@ public class ConfigurationSelector extends Composite {
         GridLayout gl = new GridLayout(4, false);
         gl.marginWidth = gl.marginHeight = 0;
         setLayout(gl);
-        
+
         // first column is the first table
         final Table fullTable = new Table(this, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
         fullTable.setLayoutData(new GridData(GridData.FILL_BOTH));
         fullTable.setHeaderVisible(true);
         fullTable.setLinesVisible(true);
-        
+
         // create the column
         final TableColumn fullTableColumn = new TableColumn(fullTable, SWT.LEFT);
         // set the header
         fullTableColumn.setText("Available Qualifiers");
-        
+
         fullTable.addControlListener(new ControlAdapter() {
             @Override
             public void controlResized(ControlEvent e) {
@@ -217,24 +223,24 @@ public class ConfigurationSelector extends Composite {
                 if (selection instanceof IStructuredSelection) {
                     IStructuredSelection structSelection = (IStructuredSelection)selection;
                     Object first = structSelection.getFirstElement();
-                    
+
                     if (first instanceof ResourceQualifier) {
                         mAddButton.setEnabled(true);
                         return;
                     }
                 }
-                
+
                 mAddButton.setEnabled(false);
             }
         });
-        
+
         // 2nd column is the left/right arrow button
         Composite buttonComposite = new Composite(this, SWT.NONE);
         gl = new GridLayout(1, false);
         gl.marginWidth = gl.marginHeight = 0;
         buttonComposite.setLayout(gl);
         buttonComposite.setLayoutData(new GridData(GridData.FILL_VERTICAL));
-        
+
         new Composite(buttonComposite, SWT.NONE);
         mAddButton = new Button(buttonComposite, SWT.BORDER | SWT.PUSH);
         mAddButton.setText("->");
@@ -242,20 +248,20 @@ public class ConfigurationSelector extends Composite {
         mAddButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
-                IStructuredSelection selection = 
+                IStructuredSelection selection =
                     (IStructuredSelection)mFullTableViewer.getSelection();
-                
+
                 Object first = selection.getFirstElement();
                 if (first instanceof ResourceQualifier) {
                     ResourceQualifier qualifier = (ResourceQualifier)first;
-                    
+
                     mBaseConfiguration.removeQualifier(qualifier);
                     mSelectedConfiguration.addQualifier(qualifier);
-                    
+
                     mFullTableViewer.refresh();
                     mSelectionTableViewer.refresh();
                     mSelectionTableViewer.setSelection(new StructuredSelection(qualifier), true);
-                    
+
                     onChange(false /* keepSelection */);
                 }
             }
@@ -267,19 +273,19 @@ public class ConfigurationSelector extends Composite {
         mRemoveButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
-                IStructuredSelection selection = 
+                IStructuredSelection selection =
                     (IStructuredSelection)mSelectionTableViewer.getSelection();
-                
+
                 Object first = selection.getFirstElement();
                 if (first instanceof ResourceQualifier) {
                     ResourceQualifier qualifier = (ResourceQualifier)first;
-                    
+
                     mSelectedConfiguration.removeQualifier(qualifier);
                     mBaseConfiguration.addQualifier(qualifier);
 
                     mFullTableViewer.refresh();
                     mSelectionTableViewer.refresh();
-                    
+
                     onChange(false /* keepSelection */);
                 }
             }
@@ -290,12 +296,12 @@ public class ConfigurationSelector extends Composite {
         selectionTable.setLayoutData(new GridData(GridData.FILL_BOTH));
         selectionTable.setHeaderVisible(true);
         selectionTable.setLinesVisible(true);
-        
+
         // create the column
         final TableColumn selectionTableColumn = new TableColumn(selectionTable, SWT.LEFT);
         // set the header
         selectionTableColumn.setText("Chosen Qualifiers");
-        
+
         selectionTable.addControlListener(new ControlAdapter() {
             @Override
             public void controlResized(ControlEvent e) {
@@ -318,22 +324,22 @@ public class ConfigurationSelector extends Composite {
                 ISelection selection = event.getSelection();
                 if (selection instanceof IStructuredSelection) {
                     IStructuredSelection structSelection = (IStructuredSelection)selection;
-                    
+
                     if (structSelection.isEmpty() == false) {
                         Object first = structSelection.getFirstElement();
-                        
+
                         if (first instanceof ResourceQualifier) {
                             mRemoveButton.setEnabled(true);
-                            
+
                             QualifierEditBase composite = mUiMap.get(first.getClass());
-    
+
                             if (composite != null) {
                                 composite.setQualifier((ResourceQualifier)first);
                             }
-    
+
                             mStackLayout.topControl = composite;
                             mQualifierEditParent.layout();
-                            
+
                             return;
                         }
                     } else {
@@ -341,21 +347,23 @@ public class ConfigurationSelector extends Composite {
                         mQualifierEditParent.layout();
                     }
                 }
-                
+
                 mRemoveButton.setEnabled(false);
             }
         });
-        
-        // 4th column is the detail of the selected qualifier 
+
+        // 4th column is the detail of the selected qualifier
         mQualifierEditParent = new Composite(this, SWT.NONE);
         mQualifierEditParent.setLayout(mStackLayout = new StackLayout());
         mQualifierEditParent.setLayoutData(new GridData(GridData.FILL_VERTICAL));
-        
+
         // create the UI for all the qualifiers, and associate them to the ResourceQualifer class.
         mUiMap.put(CountryCodeQualifier.class, new MCCEdit(mQualifierEditParent));
         mUiMap.put(NetworkCodeQualifier.class, new MNCEdit(mQualifierEditParent));
         mUiMap.put(LanguageQualifier.class, new LanguageEdit(mQualifierEditParent));
         mUiMap.put(RegionQualifier.class, new RegionEdit(mQualifierEditParent));
+        mUiMap.put(ScreenSizeQualifier.class, new ScreenSizeEdit(mQualifierEditParent));
+        mUiMap.put(ScreenRatioQualifier.class, new ScreenRatioEdit(mQualifierEditParent));
         mUiMap.put(ScreenOrientationQualifier.class, new OrientationEdit(mQualifierEditParent));
         mUiMap.put(PixelDensityQualifier.class, new PixelDensityEdit(mQualifierEditParent));
         mUiMap.put(TouchScreenQualifier.class, new TouchEdit(mQualifierEditParent));
@@ -363,8 +371,9 @@ public class ConfigurationSelector extends Composite {
         mUiMap.put(TextInputMethodQualifier.class, new TextInputEdit(mQualifierEditParent));
         mUiMap.put(NavigationMethodQualifier.class, new NavigationEdit(mQualifierEditParent));
         mUiMap.put(ScreenDimensionQualifier.class, new ScreenDimensionEdit(mQualifierEditParent));
+        mUiMap.put(VersionQualifier.class, new VersionEdit(mQualifierEditParent));
     }
-    
+
     /**
      * Sets a listener to be notified when the configuration changes.
      * @param listener A {@link Runnable} whose <code>run()</code> method is called when the
@@ -373,7 +382,7 @@ public class ConfigurationSelector extends Composite {
     public void setOnChangeListener(Runnable listener) {
         mOnChangeListener = listener;
     }
-    
+
     /**
      * Initialize the UI with a given {@link FolderConfiguration}. This must
      * be called from the UI thread.
@@ -382,24 +391,24 @@ public class ConfigurationSelector extends Composite {
     public void setConfiguration(FolderConfiguration config) {
         mSelectedConfiguration.set(config);
         mSelectionTableViewer.refresh();
-        
+
         // create the base config, which is the default config minus the qualifiers
         // in SelectedConfiguration
         mBaseConfiguration.substract(mSelectedConfiguration);
         mFullTableViewer.refresh();
     }
-    
+
     /**
      * Initialize the UI with the configuration represented by a resource folder name.
      * This must be called from the UI thread.
-     * 
+     *
      * @param folderSegments the segments of the folder name,
      *                       split using {@link FolderConfiguration#QUALIFIER_SEP}.
      * @return true if success, or false if the folder name is not a valid name.
      */
     public boolean setConfiguration(String[] folderSegments) {
         FolderConfiguration config = ResourceManager.getInstance().getConfig(folderSegments);
-        
+
         if (config == null) {
             return false;
         }
@@ -408,7 +417,7 @@ public class ConfigurationSelector extends Composite {
 
         return true;
     }
-    
+
     /**
      * Initialize the UI with the configuration represented by a resource folder name.
      * This must be called from the UI thread.
@@ -421,7 +430,7 @@ public class ConfigurationSelector extends Composite {
 
         return setConfiguration(folderSegments);
     }
-    
+
     /**
      * Gets the configuration as setup by the widget.
      * @param config the {@link FolderConfiguration} object to be filled with the information
@@ -430,7 +439,7 @@ public class ConfigurationSelector extends Composite {
     public void getConfiguration(FolderConfiguration config) {
         config.set(mSelectedConfiguration);
     }
-    
+
     /**
      * Returns the state of the configuration being edited/created.
      */
@@ -438,14 +447,14 @@ public class ConfigurationSelector extends Composite {
         if (mSelectedConfiguration.getInvalidQualifier() != null) {
             return ConfigurationState.INVALID_CONFIG;
         }
-        
+
         if (mSelectedConfiguration.checkRegion() == false) {
             return ConfigurationState.REGION_WITHOUT_LANGUAGE;
         }
-        
+
         return ConfigurationState.OK;
     }
-    
+
     /**
      * Returns the first invalid qualifier of the configuration being edited/created,
      * or <code>null<code> if they are all valid (or if none exists).
@@ -469,7 +478,7 @@ public class ConfigurationSelector extends Composite {
         }
 
         mSelectionTableViewer.refresh(true);
-        
+
         if (keepSelection) {
             mSelectionTableViewer.setSelection(selection);
             mOnRefresh = false;
@@ -479,12 +488,12 @@ public class ConfigurationSelector extends Composite {
             mOnChangeListener.run();
         }
     }
-    
+
     /**
      * Content provider around a {@link FolderConfiguration}.
      */
     private static class QualifierContentProvider implements IStructuredContentProvider {
-        
+
         private FolderConfiguration mInput;
 
         public QualifierContentProvider() {
@@ -505,12 +514,12 @@ public class ConfigurationSelector extends Composite {
             }
         }
     }
-    
+
     /**
      * Label provider for {@link ResourceQualifier} objects.
      */
     private static class QualifierLabelProvider implements ITableLabelProvider {
-        
+
         private final boolean mShowQualifierValue;
 
         public QualifierLabelProvider(boolean showQualifierValue) {
@@ -528,7 +537,7 @@ public class ConfigurationSelector extends Composite {
                     } else {
                         return value;
                     }
-                    
+
                 } else {
                     return ((ResourceQualifier)element).getShortName();
                 }
@@ -536,7 +545,7 @@ public class ConfigurationSelector extends Composite {
 
             return null;
         }
-        
+
         public Image getColumnImage(Object element, int columnIndex) {
             // only one column, so we can ignore columnIndex
             if (element instanceof ResourceQualifier) {
@@ -563,7 +572,7 @@ public class ConfigurationSelector extends Composite {
             // pass
         }
     }
-    
+
     /**
      * Base class for Edit widget for {@link ResourceQualifier}.
      */
@@ -575,10 +584,10 @@ public class ConfigurationSelector extends Composite {
 
             new Label(this, SWT.NONE).setText(title);
         }
-        
+
         public abstract void setQualifier(ResourceQualifier qualifier);
     }
-    
+
     /**
      * Edit widget for {@link CountryCodeQualifier}.
      */
@@ -588,13 +597,13 @@ public class ConfigurationSelector extends Composite {
 
         public MCCEdit(Composite parent) {
             super(parent, CountryCodeQualifier.NAME);
-            
+
             mText = new Text(this, SWT.BORDER);
             mText.addVerifyListener(new MobileCodeVerifier());
             mText.addModifyListener(new ModifyListener() {
                 public void modifyText(ModifyEvent e) {
                     onTextChange();
-                } 
+                }
             });
 
             mText.addFocusListener(new FocusAdapter() {
@@ -603,13 +612,13 @@ public class ConfigurationSelector extends Composite {
                     onTextChange();
                 }
             });
-            
+
             new Label(this, SWT.NONE).setText("(3 digit code)");
         }
-        
+
         private void onTextChange() {
             String value = mText.getText();
-            
+
             if (value.length() == 0) {
                 // empty string, means a qualifier with no value.
                 // Since the qualifier classes are immutable, and we don't want to
@@ -632,7 +641,7 @@ public class ConfigurationSelector extends Composite {
                     mSelectedConfiguration.setCountryCodeQualifier(new CountryCodeQualifier());
                 }
             }
-   
+
             // notify of change
             onChange(true /* keepSelection */);
         }
@@ -640,7 +649,7 @@ public class ConfigurationSelector extends Composite {
         @Override
         public void setQualifier(ResourceQualifier qualifier) {
             CountryCodeQualifier q = (CountryCodeQualifier)qualifier;
-            
+
             mText.setText(Integer.toString(q.getCode()));
         }
     }
@@ -653,7 +662,7 @@ public class ConfigurationSelector extends Composite {
 
         public MNCEdit(Composite parent) {
             super(parent, NetworkCodeQualifier.NAME);
-            
+
             mText = new Text(this, SWT.BORDER);
             mText.addVerifyListener(new MobileCodeVerifier());
             mText.addModifyListener(new ModifyListener() {
@@ -670,10 +679,10 @@ public class ConfigurationSelector extends Composite {
 
             new Label(this, SWT.NONE).setText("(1-3 digit code)");
         }
-        
+
         private void onTextChange() {
             String value = mText.getText();
-            
+
             if (value.length() == 0) {
                 // empty string, means a qualifier with no value.
                 // Since the qualifier classes are immutable, and we don't want to
@@ -696,19 +705,19 @@ public class ConfigurationSelector extends Composite {
                     mSelectedConfiguration.setNetworkCodeQualifier(new NetworkCodeQualifier());
                 }
             }
-   
+
             // notify of change
             onChange(true /* keepSelection */);
-        } 
+        }
 
         @Override
         public void setQualifier(ResourceQualifier qualifier) {
             NetworkCodeQualifier q = (NetworkCodeQualifier)qualifier;
-            
+
             mText.setText(Integer.toString(q.getCode()));
         }
     }
-    
+
     /**
      * Edit widget for {@link LanguageQualifier}.
      */
@@ -741,7 +750,7 @@ public class ConfigurationSelector extends Composite {
         private void onLanguageChange() {
             // update the current config
             String value = mLanguage.getText();
-            
+
             if (value.length() == 0) {
                 // empty string, means no qualifier.
                 // Since the qualifier classes are immutable, and we don't want to
@@ -809,7 +818,7 @@ public class ConfigurationSelector extends Composite {
         private void onRegionChange() {
             // update the current config
             String value = mRegion.getText();
-            
+
             if (value.length() == 0) {
                 // empty string, means no qualifier.
                 // Since the qualifier classes are immutable, and we don't want to
@@ -844,7 +853,125 @@ public class ConfigurationSelector extends Composite {
             }
         }
     }
-    
+
+    /**
+     * Edit widget for {@link ScreenSizeQualifier}.
+     */
+    private class ScreenSizeEdit extends QualifierEditBase {
+
+        private Combo mSize;
+
+        public ScreenSizeEdit(Composite parent) {
+            super(parent, ScreenSizeQualifier.NAME);
+
+            mSize = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+            ScreenSize[] ssValues = ScreenSize.values();
+            for (ScreenSize value : ssValues) {
+                mSize.add(value.getDisplayValue());
+            }
+
+            mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+            mSize.addSelectionListener(new SelectionListener() {
+                public void widgetDefaultSelected(SelectionEvent e) {
+                    onScreenSizeChange();
+                }
+                public void widgetSelected(SelectionEvent e) {
+                    onScreenSizeChange();
+                }
+            });
+        }
+
+        protected void onScreenSizeChange() {
+            // update the current config
+            int index = mSize.getSelectionIndex();
+
+            if (index != -1) {
+                mSelectedConfiguration.setScreenSizeQualifier(new ScreenSizeQualifier(
+                    ScreenSize.getByIndex(index)));
+            } else {
+                // empty selection, means no qualifier.
+                // Since the qualifier classes are immutable, and we don't want to
+                // remove the qualifier from the configuration, we create a new default one.
+                mSelectedConfiguration.setScreenSizeQualifier(
+                        new ScreenSizeQualifier());
+            }
+
+            // notify of change
+            onChange(true /* keepSelection */);
+        }
+
+        @Override
+        public void setQualifier(ResourceQualifier qualifier) {
+            ScreenSizeQualifier q = (ScreenSizeQualifier)qualifier;
+
+            ScreenSize value = q.getValue();
+            if (value == null) {
+                mSize.clearSelection();
+            } else {
+                mSize.select(ScreenSize.getIndex(value));
+            }
+        }
+    }
+
+    /**
+     * Edit widget for {@link ScreenRatioQualifier}.
+     */
+    private class ScreenRatioEdit extends QualifierEditBase {
+
+        private Combo mSize;
+
+        public ScreenRatioEdit(Composite parent) {
+            super(parent, ScreenRatioQualifier.NAME);
+
+            mSize = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+            ScreenRatio[] srValues = ScreenRatio.values();
+            for (ScreenRatio value : srValues) {
+                mSize.add(value.getDisplayValue());
+            }
+
+            mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+            mSize.addSelectionListener(new SelectionListener() {
+                public void widgetDefaultSelected(SelectionEvent e) {
+                    onScreenRatioChange();
+                }
+                public void widgetSelected(SelectionEvent e) {
+                    onScreenRatioChange();
+                }
+            });
+        }
+
+        protected void onScreenRatioChange() {
+            // update the current config
+            int index = mSize.getSelectionIndex();
+
+            if (index != -1) {
+                mSelectedConfiguration.setScreenRatioQualifier(new ScreenRatioQualifier(
+                        ScreenRatio.getByIndex(index)));
+            } else {
+                // empty selection, means no qualifier.
+                // Since the qualifier classes are immutable, and we don't want to
+                // remove the qualifier from the configuration, we create a new default one.
+                mSelectedConfiguration.setScreenRatioQualifier(
+                        new ScreenRatioQualifier());
+            }
+
+            // notify of change
+            onChange(true /* keepSelection */);
+        }
+
+        @Override
+        public void setQualifier(ResourceQualifier qualifier) {
+            ScreenRatioQualifier q = (ScreenRatioQualifier)qualifier;
+
+            ScreenRatio value = q.getValue();
+            if (value == null) {
+                mSize.clearSelection();
+            } else {
+                mSize.select(ScreenRatio.getIndex(value));
+            }
+        }
+    }
+
     /**
      * Edit widget for {@link ScreenOrientationQualifier}.
      */
@@ -908,63 +1035,58 @@ public class ConfigurationSelector extends Composite {
      * Edit widget for {@link PixelDensityQualifier}.
      */
     private class PixelDensityEdit extends QualifierEditBase {
-        private Text mText;
+        private Combo mDensity;
 
         public PixelDensityEdit(Composite parent) {
             super(parent, PixelDensityQualifier.NAME);
-            
-            mText = new Text(this, SWT.BORDER);
-            mText.addVerifyListener(new DensityVerifier());
-            mText.addModifyListener(new ModifyListener() {
-                public void modifyText(ModifyEvent e) {
-                    onTextChange();
+
+            mDensity = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+            Density[] soValues = Density.values();
+            for (Density value : soValues) {
+                mDensity.add(value.getDisplayValue());
+            }
+
+            mDensity.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+            mDensity.addSelectionListener(new SelectionListener() {
+                public void widgetDefaultSelected(SelectionEvent e) {
+                    onDensityChange();
                 }
-            });
-            mText.addFocusListener(new FocusAdapter() {
-                @Override
-                public void focusLost(FocusEvent e) {
-                    onTextChange();
+                public void widgetSelected(SelectionEvent e) {
+                    onDensityChange();
                 }
             });
+
         }
-        
-        private void onTextChange() {
-            String value = mText.getText();
-            
-            if (value.length() == 0) {
-                // empty string, means a qualifier with no value.
+
+        private void onDensityChange() {
+            // update the current config
+            int index = mDensity.getSelectionIndex();
+
+            if (index != -1) {
+                mSelectedConfiguration.setPixelDensityQualifier(new PixelDensityQualifier(
+                    Density.getByIndex(index)));
+            } else {
+                // empty selection, means no qualifier.
                 // Since the qualifier classes are immutable, and we don't want to
                 // remove the qualifier from the configuration, we create a new default one.
-                mSelectedConfiguration.setPixelDensityQualifier(new PixelDensityQualifier());
-            } else {
-                try {
-                    PixelDensityQualifier qualifier = PixelDensityQualifier.getQualifier(
-                            PixelDensityQualifier.getFolderSegment(Integer.parseInt(value)));
-                    if (qualifier != null) {
-                        mSelectedConfiguration.setPixelDensityQualifier(qualifier);
-                    } else {
-                        // Failure! Looks like the value is wrong
-                        // (for instance a one letter string).
-                        // We do nothing in this case.
-                        return;
-                    }
-                } catch (NumberFormatException nfe) {
-                    // Looks like the code is not a number. This should not happen since the text
-                    // field has a VerifyListener that prevents it.
-                    // We do nothing in this case.
-                    return;
-                }
+                mSelectedConfiguration.setPixelDensityQualifier(
+                        new PixelDensityQualifier());
             }
-   
+
             // notify of change
             onChange(true /* keepSelection */);
-        } 
+        }
 
         @Override
         public void setQualifier(ResourceQualifier qualifier) {
             PixelDensityQualifier q = (PixelDensityQualifier)qualifier;
-            
-            mText.setText(Integer.toString(q.getValue()));
+
+            Density value = q.getValue();
+            if (value == null) {
+                mDensity.clearSelection();
+            } else {
+                mDensity.select(Density.getIndex(value));
+            }
         }
     }
 
@@ -1202,7 +1324,7 @@ public class ConfigurationSelector extends Composite {
             }
         }
     }
-    
+
     /**
      * Edit widget for {@link ScreenDimensionQualifier}.
      */
@@ -1217,9 +1339,9 @@ public class ConfigurationSelector extends Composite {
             ModifyListener modifyListener = new ModifyListener() {
                 public void modifyText(ModifyEvent e) {
                     onSizeChange();
-                } 
+                }
             };
-            
+
             FocusAdapter focusListener = new FocusAdapter() {
                 @Override
                 public void focusLost(FocusEvent e) {
@@ -1237,12 +1359,12 @@ public class ConfigurationSelector extends Composite {
             mSize2.addModifyListener(modifyListener);
             mSize2.addFocusListener(focusListener);
         }
-        
+
         private void onSizeChange() {
             // update the current config
             String size1 = mSize1.getText();
             String size2 = mSize2.getText();
-            
+
             if (size1.length() == 0 || size2.length() == 0) {
                 // if one of the strings is empty, reset to no qualifier.
                 // Since the qualifier classes are immutable, and we don't want to
@@ -1262,7 +1384,7 @@ public class ConfigurationSelector extends Composite {
                             new ScreenDimensionQualifier());
                 }
             }
-   
+
             // notify of change
             onChange(true /* keepSelection */);
         }
@@ -1270,9 +1392,73 @@ public class ConfigurationSelector extends Composite {
         @Override
         public void setQualifier(ResourceQualifier qualifier) {
             ScreenDimensionQualifier q = (ScreenDimensionQualifier)qualifier;
-            
+
             mSize1.setText(Integer.toString(q.getValue1()));
             mSize2.setText(Integer.toString(q.getValue2()));
         }
     }
+
+    /**
+     * Edit widget for {@link VersionQualifier}.
+     */
+    private class VersionEdit extends QualifierEditBase {
+        private Text mText;
+
+        public VersionEdit(Composite parent) {
+            super(parent, VersionQualifier.NAME);
+
+            mText = new Text(this, SWT.BORDER);
+            mText.addVerifyListener(new MobileCodeVerifier());
+            mText.addModifyListener(new ModifyListener() {
+                public void modifyText(ModifyEvent e) {
+                    onVersionChange();
+                }
+            });
+            mText.addFocusListener(new FocusAdapter() {
+                @Override
+                public void focusLost(FocusEvent e) {
+                    onVersionChange();
+                }
+            });
+
+            new Label(this, SWT.NONE).setText("(Platform API level)");
+        }
+
+        private void onVersionChange() {
+            String value = mText.getText();
+
+            if (value.length() == 0) {
+                // empty string, means a qualifier with no value.
+                // Since the qualifier classes are immutable, and we don't want to
+                // remove the qualifier from the configuration, we create a new default one.
+                mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
+            } else {
+                try {
+                    VersionQualifier qualifier = VersionQualifier.getQualifier(
+                            VersionQualifier.getFolderSegment(Integer.parseInt(value)));
+                    if (qualifier != null) {
+                        mSelectedConfiguration.setVersionQualifier(qualifier);
+                    } else {
+                        // Failure! Looks like the value is wrong
+                        mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
+                    }
+                } catch (NumberFormatException nfe) {
+                    // Looks like the code is not a number. This should not happen since the text
+                    // field has a VerifyListener that prevents it.
+                    mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
+                }
+            }
+
+            // notify of change
+            onChange(true /* keepSelection */);
+        }
+
+        @Override
+        public void setQualifier(ResourceQualifier qualifier) {
+            VersionQualifier q = (VersionQualifier)qualifier;
+
+            mText.setText(Integer.toString(q.getVersion()));
+        }
+    }
+
 }
index 834d5e8..15e9912 100644 (file)
 
 package com.android.ide.eclipse.adt.internal.wizards.export;
 
+import com.android.ide.eclipse.adt.AdtConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
 import com.android.jarutils.KeystoreHelper;
 import com.android.jarutils.SignedJarBuilder;
 import com.android.jarutils.DebugKeyProvider.IKeyGenOutput;
-import com.android.jarutils.DebugKeyProvider.KeytoolException;
 
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IncrementalProjectBuilder;
-import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.jface.operation.IRunnableWithProgress;
@@ -43,15 +42,16 @@ import org.eclipse.ui.IExportWizard;
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.PlatformUI;
 
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
 import java.lang.reflect.InvocationTargetException;
-import java.security.GeneralSecurityException;
 import java.security.KeyStore;
-import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.KeyStore.PrivateKeyEntry;
 import java.security.cert.X509Certificate;
@@ -62,23 +62,23 @@ import java.util.Set;
 import java.util.Map.Entry;
 
 /**
- * Export wizard to export an apk signed with a release key/certificate. 
+ * Export wizard to export an apk signed with a release key/certificate.
  */
 public final class ExportWizard extends Wizard implements IExportWizard {
 
     private static final String PROJECT_LOGO_LARGE = "icons/android_large.png"; //$NON-NLS-1$
-    
+
     private static final String PAGE_PROJECT_CHECK = "Page_ProjectCheck"; //$NON-NLS-1$
     private static final String PAGE_KEYSTORE_SELECTION = "Page_KeystoreSelection"; //$NON-NLS-1$
     private static final String PAGE_KEY_CREATION = "Page_KeyCreation"; //$NON-NLS-1$
     private static final String PAGE_KEY_SELECTION = "Page_KeySelection"; //$NON-NLS-1$
     private static final String PAGE_KEY_CHECK = "Page_KeyCheck"; //$NON-NLS-1$
-    
+
     static final String PROPERTY_KEYSTORE = "keystore"; //$NON-NLS-1$
     static final String PROPERTY_ALIAS = "alias"; //$NON-NLS-1$
     static final String PROPERTY_DESTINATION = "destination"; //$NON-NLS-1$
     static final String PROPERTY_FILENAME = "baseFilename"; //$NON-NLS-1$
-    
+
     static final int APK_FILE_SOURCE = 0;
     static final int APK_FILE_DEST = 1;
     static final int APK_COUNT = 2;
@@ -87,7 +87,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
      * Base page class for the ExportWizard page. This class add the {@link #onShow()} callback.
      */
     static abstract class ExportWizardPage extends WizardPage {
-        
+
         /** bit mask constant for project data change event */
         protected static final int DATA_PROJECT = 0x001;
         /** bit mask constant for keystore data change event */
@@ -99,13 +99,13 @@ public final class ExportWizard extends Wizard implements IExportWizard {
             public void verifyText(VerifyEvent e) {
                 // verify the characters are valid for password.
                 int len = e.text.length();
-                
+
                 // first limit to 127 characters max
                 if (len + ((Text)e.getSource()).getText().length() > 127) {
                     e.doit = false;
                     return;
                 }
-                
+
                 // now only take non control characters
                 for (int i = 0 ; i < len ; i++) {
                     if (e.text.charAt(i) < 32) {
@@ -115,7 +115,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                 }
             }
         };
-        
+
         /**
          * Bit mask indicating what changed while the page was hidden.
          * @see #DATA_PROJECT
@@ -123,13 +123,13 @@ public final class ExportWizard extends Wizard implements IExportWizard {
          * @see #DATA_KEY
          */
         protected int mProjectDataChanged = 0;
-        
+
         ExportWizardPage(String name) {
             super(name);
         }
-        
+
         abstract void onShow();
-        
+
         @Override
         public void setVisible(boolean visible) {
             super.setVisible(visible);
@@ -138,23 +138,23 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                 mProjectDataChanged = 0;
             }
         }
-        
+
         final void projectDataChanged(int changeMask) {
             mProjectDataChanged |= changeMask;
         }
-        
+
         /**
          * Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a
          * {@link Throwable} object.
          */
         protected void onException(Throwable t) {
             String message = getExceptionMessage(t);
-            
+
             setErrorMessage(message);
             setPageComplete(false);
         }
     }
-    
+
     private ExportWizardPage mPages[] = new ExportWizardPage[5];
 
     private IProject mProject;
@@ -189,7 +189,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
         setWindowTitle("Export Android Application");
         setImageDescriptor();
     }
-    
+
     @Override
     public void addPages() {
         addPage(mPages[0] = new ProjectCheckPage(this, PAGE_PROJECT_CHECK));
@@ -209,7 +209,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                 mDestinationParentFolder.getAbsolutePath());
         ProjectHelper.saveStringProperty(mProject, PROPERTY_FILENAME,
                 mApkMap.get(null)[APK_FILE_DEST]);
-        
+
         // run the export in an UI runnable.
         IWorkbench workbench = PlatformUI.getWorkbench();
         final boolean[] result = new boolean[1];
@@ -234,10 +234,10 @@ public final class ExportWizard extends Wizard implements IExportWizard {
         } catch (InterruptedException e) {
             return false;
         }
-        
+
         return result[0];
     }
-    
+
     private boolean doExport(IProgressMonitor monitor) {
         try {
             // first we make sure the project is built
@@ -262,13 +262,13 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                                 output.add(message);
                             }
                         });
-                
+
                 if (createdStore == false) {
                     // keystore creation error!
                     displayError(output.toArray(new String[output.size()]));
                     return false;
                 }
-                
+
                 // keystore is created, now load the private key and certificate.
                 KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                 FileInputStream fis = new FileInputStream(mKeystore);
@@ -276,7 +276,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                 fis.close();
                 PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
                         mKeyAlias, new KeyStore.PasswordProtection(mKeyPassword.toCharArray()));
-                
+
                 if (entry != null) {
                     mPrivateKey = entry.getPrivateKey();
                     mCertificate = (X509Certificate)entry.getCertificate();
@@ -287,7 +287,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                     return false;
                 }
             }
-            
+
             // check the private key/certificate again since it may have been created just above.
             if (mPrivateKey != null && mCertificate != null) {
                 // get the output folder of the project to export.
@@ -297,27 +297,50 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                     return false;
                 }
                 String outputOsPath =  outputIFolder.getLocation().toOSString();
-                
+
                 // now generate the packages.
                 Set<Entry<String, String[]>> set = mApkMap.entrySet();
+
+                boolean runZipAlign = false;
+                String path = AdtPlugin.getOsAbsoluteZipAlign();
+                File zipalign = new File(path);
+                runZipAlign = zipalign.isFile();
+
                 for (Entry<String, String[]> entry : set) {
                     String[] defaultApk = entry.getValue();
                     String srcFilename = defaultApk[APK_FILE_SOURCE];
                     String destFilename = defaultApk[APK_FILE_DEST];
+                    File destFile;
+                    if (runZipAlign) {
+                        destFile = File.createTempFile("android", ".apk");
+                    } else {
+                        destFile = new File(mDestinationParentFolder, destFilename);
+                    }
+
 
-                    FileOutputStream fos = new FileOutputStream(
-                            new File(mDestinationParentFolder, destFilename));
+                    FileOutputStream fos = new FileOutputStream(destFile);
                     SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate);
-                    
+
                     // get the input file.
                     FileInputStream fis = new FileInputStream(new File(outputOsPath, srcFilename));
-                    
+
                     // add the content of the source file to the output file, and sign it at
                     // the same time.
                     try {
                         builder.writeZip(fis, null /* filter */);
-                        // close the builder: write the final signature files, and close the archive.
+                        // close the builder: write the final signature files,
+                        // and close the archive.
                         builder.close();
+
+                        // now zipalign the result
+                        if (runZipAlign) {
+                            File realDestFile = new File(mDestinationParentFolder, destFilename);
+                            String message = zipAlign(path, destFile, realDestFile);
+                            if (message != null) {
+                                displayError(message);
+                                return false;
+                            }
+                        }
                     } finally {
                         try {
                             fis.close();
@@ -326,25 +349,26 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                         }
                     }
                 }
+
+                // export success. In case we didn't run ZipAlign we display a warning
+                if (runZipAlign == false) {
+                    AdtPlugin.displayWarning("Export Wizard",
+                            "The zipalign tool was not found in the SDK.\n\n" +
+                            "Please update to the latest SDK and re-export your application\n" +
+                            "or run zipalign manually.\n\n" +
+                            "Aligning applications allows Android to use application resources\n" +
+                            "more efficiently.");
+                }
+
                 return true;
             }
-        } catch (FileNotFoundException e) {
-            displayError(e);
-        } catch (NoSuchAlgorithmException e) {
-            displayError(e);
-        } catch (IOException e) {
-            displayError(e);
-        } catch (GeneralSecurityException e) {
-            displayError(e);
-        } catch (KeytoolException e) {
-            displayError(e);
-        } catch (CoreException e) {
-            displayError(e);
+        } catch (Throwable t) {
+            displayError(t);
         }
 
         return false;
     }
-    
+
     @Override
     public boolean canFinish() {
         // check if we have the apk to resign, the destination location, and either
@@ -356,7 +380,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                         || mKeystoreCreationMode || mKeyCreationMode) &&
                 mDestinationParentFolder != null;
     }
-    
+
     /*
      * (non-Javadoc)
      * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
@@ -364,7 +388,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
     public void init(IWorkbench workbench, IStructuredSelection selection) {
         // get the project from the selection
         Object selected = selection.getFirstElement();
-        
+
         if (selected instanceof IProject) {
             mProject = (IProject)selected;
         } else if (selected instanceof IAdaptable) {
@@ -374,19 +398,19 @@ public final class ExportWizard extends Wizard implements IExportWizard {
             }
         }
     }
-    
+
     ExportWizardPage getKeystoreSelectionPage() {
         return mKeystoreSelectionPage;
     }
-    
+
     ExportWizardPage getKeyCreationPage() {
         return mKeyCreationPage;
     }
-    
+
     ExportWizardPage getKeySelectionPage() {
         return mKeySelectionPage;
     }
-    
+
     ExportWizardPage getKeyCheckPage() {
         return mKeyCheckPage;
     }
@@ -398,39 +422,39 @@ public final class ExportWizard extends Wizard implements IExportWizard {
         ImageDescriptor desc = AdtPlugin.getImageDescriptor(PROJECT_LOGO_LARGE);
         setDefaultPageImageDescriptor(desc);
     }
-    
+
     IProject getProject() {
         return mProject;
     }
-    
+
     void setProject(IProject project) {
         mProject = project;
-        
+
         updatePageOnChange(ExportWizardPage.DATA_PROJECT);
     }
-    
+
     void setKeystore(String path) {
         mKeystore = path;
         mPrivateKey = null;
         mCertificate = null;
-        
+
         updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
     }
-    
+
     String getKeystore() {
         return mKeystore;
     }
-    
+
     void setKeystoreCreationMode(boolean createStore) {
         mKeystoreCreationMode = createStore;
         updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
     }
-    
+
     boolean getKeystoreCreationMode() {
         return mKeystoreCreationMode;
     }
-    
-    
+
+
     void setKeystorePassword(String password) {
         mKeystorePassword = password;
         mPrivateKey = null;
@@ -438,7 +462,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
 
         updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
     }
-    
+
     String getKeystorePassword() {
         return mKeystorePassword;
     }
@@ -447,15 +471,15 @@ public final class ExportWizard extends Wizard implements IExportWizard {
         mKeyCreationMode = createKey;
         updatePageOnChange(ExportWizardPage.DATA_KEY);
     }
-    
+
     boolean getKeyCreationMode() {
         return mKeyCreationMode;
     }
-    
+
     void setExistingAliases(List<String> aliases) {
         mExistingAliases = aliases;
     }
-    
+
     List<String> getExistingAliases() {
         return mExistingAliases;
     }
@@ -467,7 +491,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
 
         updatePageOnChange(ExportWizardPage.DATA_KEY);
     }
-    
+
     String getKeyAlias() {
         return mKeyAlias;
     }
@@ -479,7 +503,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
 
         updatePageOnChange(ExportWizardPage.DATA_KEY);
     }
-    
+
     String getKeyPassword() {
         return mKeyPassword;
     }
@@ -488,7 +512,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
         mValidity = validity;
         updatePageOnChange(ExportWizardPage.DATA_KEY);
     }
-    
+
     int getValidity() {
         return mValidity;
     }
@@ -497,7 +521,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
         mDName = dName;
         updatePageOnChange(ExportWizardPage.DATA_KEY);
     }
-    
+
     String getDName() {
         return mDName;
     }
@@ -522,7 +546,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
             page.projectDataChanged(changeMask);
         }
     }
-    
+
     private void displayError(String... messages) {
         String message = null;
         if (messages.length == 1) {
@@ -533,20 +557,125 @@ public final class ExportWizard extends Wizard implements IExportWizard {
                 sb.append('\n');
                 sb.append(messages[i]);
             }
-            
+
             message = sb.toString();
         }
 
         AdtPlugin.displayError("Export Wizard", message);
     }
-    
-    private void displayError(Exception e) {
-        String message = getExceptionMessage(e);
+
+    private void displayError(Throwable t) {
+        String message = getExceptionMessage(t);
         displayError(message);
-        
-        AdtPlugin.log(e, "Export Wizard Error");
+
+        AdtPlugin.log(t, "Export Wizard Error");
     }
-    
+
+    /**
+     * Executes zipalign
+     * @param zipAlignPath location of the zipalign too
+     * @param source file to zipalign
+     * @param destination where to write the resulting file
+     * @return null if success, the error otherwise
+     * @throws IOException
+     */
+    private String zipAlign(String zipAlignPath, File source, File destination) throws IOException {
+        // command line: zipaling -f 4 tmp destination
+        String[] command = new String[5];
+        command[0] = zipAlignPath;
+        command[1] = "-f"; //$NON-NLS-1$
+        command[2] = "4"; //$NON-NLS-1$
+        command[3] = source.getAbsolutePath();
+        command[4] = destination.getAbsolutePath();
+
+        Process process = Runtime.getRuntime().exec(command);
+        ArrayList<String> output = new ArrayList<String>();
+        try {
+            if (grabProcessOutput(process, output) != 0) {
+                // build a single message from the array list
+                StringBuilder sb = new StringBuilder("Error while running zipalign:");
+                for (String msg : output) {
+                    sb.append('\n');
+                    sb.append(msg);
+                }
+
+                return sb.toString();
+            }
+        } catch (InterruptedException e) {
+            // ?
+        }
+        return null;
+    }
+
+    /**
+     * Get the stderr output of a process and return when the process is done.
+     * @param process The process to get the ouput from
+     * @param results The array to store the stderr output
+     * @return the process return code.
+     * @throws InterruptedException
+     */
+    private final int grabProcessOutput(final Process process,
+            final ArrayList<String> results)
+            throws InterruptedException {
+        // Due to the limited buffer size on windows for the standard io (stderr, stdout), we
+        // *need* to read both stdout and stderr all the time. If we don't and a process output
+        // a large amount, this could deadlock the process.
+
+        // read the lines as they come. if null is returned, it's
+        // because the process finished
+        new Thread("") { //$NON-NLS-1$
+            @Override
+            public void run() {
+                // create a buffer to read the stderr output
+                InputStreamReader is = new InputStreamReader(process.getErrorStream());
+                BufferedReader errReader = new BufferedReader(is);
+
+                try {
+                    while (true) {
+                        String line = errReader.readLine();
+                        if (line != null) {
+                            results.add(line);
+                        } else {
+                            break;
+                        }
+                    }
+                } catch (IOException e) {
+                    // do nothing.
+                }
+            }
+        }.start();
+
+        new Thread("") { //$NON-NLS-1$
+            @Override
+            public void run() {
+                InputStreamReader is = new InputStreamReader(process.getInputStream());
+                BufferedReader outReader = new BufferedReader(is);
+
+                IProject project = getProject();
+
+                try {
+                    while (true) {
+                        String line = outReader.readLine();
+                        if (line != null) {
+                            AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
+                                    project, line);
+                        } else {
+                            break;
+                        }
+                    }
+                } catch (IOException e) {
+                    // do nothing.
+                }
+            }
+
+        }.start();
+
+        // get the return code from the process
+        return process.waitFor();
+    }
+
+
+
     /**
      * Returns the {@link Throwable#getMessage()}. If the {@link Throwable#getMessage()} returns
      * <code>null</code>, the method is called again on the cause of the Throwable object.
@@ -556,15 +685,13 @@ public final class ExportWizard extends Wizard implements IExportWizard {
     static String getExceptionMessage(Throwable t) {
         String message = t.getMessage();
         if (message == null) {
-            Throwable cause = t.getCause();
-            if (cause != null) {
-                return getExceptionMessage(cause);
-            }
-
-            // no more cause and still no message. display the first exception.
-            return t.getClass().getCanonicalName();
+            // no error info? get the stack call to display it
+            // At least that'll give us a better bug report.
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            t.printStackTrace(new PrintStream(baos));
+            message = baos.toString();
         }
-        
+
         return message;
     }
 }
index 4edfb77..bd4e7a3 100644 (file)
@@ -897,6 +897,9 @@ class NewXmlFileCreationPage extends WizardPage {
         // enable types based on new API level
         enableTypesBasedOnApi();
 
+        // update the folder name based on API level
+        resetFolderPath(false /*validate*/);
+
         // update the Type with the new descriptors.
         initializeRootValues();
 
@@ -1023,7 +1026,7 @@ class NewXmlFileCreationPage extends WizardPage {
             // The configuration is valid. Reformat the folder path using the canonical
             // value from the configuration.
 
-            newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType());
+            newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType(), mProject);
         } else {
             // The configuration is invalid. We still update the path but this time
             // do it manually on the string.
@@ -1032,7 +1035,8 @@ class NewXmlFileCreationPage extends WizardPage {
                         "^(" + RES_FOLDER_ABS +")[^-]*(.*)",         //$NON-NLS-1$ //$NON-NLS-2$
                         "\\1" + type.getResFolderName() + "\\2");   //$NON-NLS-1$ //$NON-NLS-2$
             } else {
-                newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType());
+                newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType(),
+                        mProject);
             }
         }
 
@@ -1085,19 +1089,7 @@ class NewXmlFileCreationPage extends WizardPage {
                 return;
             }
 
-            TypeInfo type = getSelectedType();
-
-            if (type != null) {
-                mConfigSelector.getConfiguration(mTempConfig);
-                StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
-                sb.append(mTempConfig.getFolderName(type.getResFolderType()));
-
-                mInternalWsFolderPathUpdate = true;
-                mWsFolderPathTextField.setText(sb.toString());
-                mInternalWsFolderPathUpdate = false;
-
-                validatePage();
-            }
+            resetFolderPath(true /*validate*/);
         }
     }
 
@@ -1139,6 +1131,28 @@ class NewXmlFileCreationPage extends WizardPage {
     }
 
     /**
+     * Reset the current Folder path based on the UI selection
+     * @param validate if true, force a call to {@link #validatePage()}.
+     */
+    private void resetFolderPath(boolean validate) {
+        TypeInfo type = getSelectedType();
+
+        if (type != null) {
+            mConfigSelector.getConfiguration(mTempConfig);
+            StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
+            sb.append(mTempConfig.getFolderName(type.getResFolderType(), mProject));
+
+            mInternalWsFolderPathUpdate = true;
+            mWsFolderPathTextField.setText(sb.toString());
+            mInternalWsFolderPathUpdate = false;
+
+            if (validate) {
+                validatePage();
+            }
+        }
+    }
+
+    /**
      * Validates the fields, displays errors and warnings.
      * Enables the finish button if there are no errors.
      */
index ebdec6d..9f021ae 100644 (file)
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: Dalvik Debug Monitor Service
 Bundle-SymbolicName: com.android.ide.eclipse.ddms;singleton:=true
-Bundle-Version: 0.9.2.qualifier
+Bundle-Version: 0.9.3.qualifier
 Bundle-Activator: com.android.ide.eclipse.ddms.DdmsPlugin
 Bundle-Vendor: The Android Open Source Project
 Bundle-Localization: plugin
index 6f2a534..5762668 100644 (file)
@@ -6,5 +6,6 @@
        <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
        <classpathentry kind="lib" path="kxml2-2.3.0.jar"/>
        <classpathentry kind="lib" path="/adt/sdklib.jar" sourcepath="/SdkLib"/>
+       <classpathentry kind="lib" path="/adt/layoutlib_api.jar" sourcepath="/layoutlib_api"/>
        <classpathentry kind="output" path="bin"/>
 </classpath>
index 61bfbe4..a93bdef 100644 (file)
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: Android Plugin Tests
 Bundle-SymbolicName: com.android.ide.eclipse.tests
-Bundle-Version: 0.9.2.qualifier
+Bundle-Version: 0.9.3.qualifier
 Bundle-Activator: com.android.ide.eclipse.tests.AndroidTestPlugin
 Require-Bundle: org.eclipse.ui,
  org.eclipse.core.runtime,
@@ -16,4 +16,6 @@ Require-Bundle: org.eclipse.ui,
 Bundle-ActivationPolicy: lazy
 Bundle-Vendor: The Android Open Source Project
 Bundle-ClassPath: kxml2-2.3.0.jar,
- .
+ .,
+ layoutlib_api.jar,
+ sdklib.jar
index f5899e3..fe69958 100644 (file)
@@ -1,20 +1,23 @@
 This project contains the tests for the Android Eclipse Plugins.
 
-You can do two things:
-1- Run the full "eclipse plugin" suite
-2- Run independent JUnit tests (not as plugin)
+You can do three things:
+1- Run the unit tests as a full "eclipse plugin" suite
+2- Run the unit tests as independent JUnit tests (not as plugin)
+3. Run the functional tests as a full "eclipse plugin" suite (require a real SDK)
+
+The unit tests are isolated tests that do not require external dependencies such as an SDK. 
+The functional tests are higher level tests that may require a real SDK.
 
 ------------------------------------------
-1- Running the full "eclipse plugin" suite
+1- Running the unit tests as a full "eclipse plugin" suite
 ------------------------------------------
 
 Steps to run the test suite:
 
-A- In Eclipse, import following projects from //device/tools/eclipse/plugins:
+A- In Eclipse, import following projects from development/tools/eclipse/plugins:
        - adt-tests
        - adt
-       - common
-       - editors
+       - ddms
 
 B- Create a new "JUnit Plug-in Test" run configuration via the "Run > Open Run Dialog..." menu
 Set the launch configuration's data as follows:
@@ -44,14 +47,13 @@ in conjunction with the "test_data" environment variable mentioned above
 
 
 -------------------------------------------
-2- Run independent JUnit tests (not plugin)
+2- Run the unit tests as independent JUnit tests (not plugin)
 -------------------------------------------
 
-A- In Eclipse, import following projects from //device/tools/eclipse/plugins:
+A- In Eclipse, import following projects from development/tools/eclipse/plugins:
        - adt-tests
        - adt
-       - common
-       - editors
+       - ddms
 
 B- Select the "unittests" source folder, right-click and select
        "Run As > JUnit Test" (i.e. not the plugin tests)
@@ -67,6 +69,33 @@ continuous test environment. Tests that pass when run as "JUnit Tests" can
 fail when run as "JUnit Plugin Tests", due to the extra constraints imposed by
 running within an Eclipse plug-in noted in section 1.
 
+------------------------------------------
+3- Running the functional tests as a full "eclipse plugin" suite
+------------------------------------------
+
+Steps to run the test suite:
+
+A- In Eclipse, import following projects from development/tools/eclipse/plugins:
+       - adt-tests
+       - adt
+       - ddms
 
+B - Setup an SDK on host machine, that is compatible with the Eclipse ADT plugins under test
+
+C- Create a new "JUnit Plug-in Test" run configuration via the "Run > Open Run Dialog..." menu
+Set the launch configuration's data as follows:
+i. "Test" tab: 
+  Select "Run a single test"
+  Project: adt-tests 
+  Test class: com.android.ide.eclipse.tests.FuncTests
+  Test runner: JUnit 3
+ii. "Environment" tab:
+ Add a "sdk_home" environment variable, setting its path to the SDK from step B
+
+All other fields can be left with their default values
+
+D. Run the newly created launch configuration
+
+Running the tests will run a secondary instance of Eclipse. 
 
 
index cbfd993..8649a77 100644 (file)
@@ -7,4 +7,6 @@ bin.includes = META-INF/,\
                prefs.template,\
                unittest.xml,\
                kxml2-2.3.0.jar,\
-               unittests/data/
+               unittests/data/,\
+               sdklib.jar,\
+               layoutlib_api.jar
index 63f17ab..4322727 100644 (file)
@@ -1,12 +1,12 @@
 /*
  * Copyright (C) 2008 The Android Open Source Project
- * 
+ *
  * Licensed under the Eclipse Public License, Version 1.0 (the "License"); you
  * may not use this file except in compliance with the License. You may obtain a
  * copy of the License at
- * 
+ *
  * http://www.eclipse.org/org/documents/epl-v10.php
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -18,7 +18,7 @@ package com.android.ide.eclipse.tests;
 import junit.framework.TestCase;
 
 /**
- * Generic superclass for Eclipse Android functional test cases, that provides 
+ * Generic superclass for Eclipse Android functional test cases, that provides
  * common facilities
  */
 public class FuncTestCase extends TestCase {
@@ -27,12 +27,15 @@ public class FuncTestCase extends TestCase {
 
     /**
      * Constructor
-     * 
+     *
      * @throws IllegalArgumentException if environment variable "sdk_home" is
      *         not set
      */
     protected FuncTestCase() {
         mOsSdkLocation = System.getProperty("sdk_home");
+        if (mOsSdkLocation == null) {
+            mOsSdkLocation = System.getenv("sdk_home");
+        }
         if (mOsSdkLocation == null || mOsSdkLocation.length() < 1) {
             throw new IllegalArgumentException(
                     "Environment variable sdk_home is not set");
index 08405e8..02c9247 100644 (file)
@@ -1,12 +1,12 @@
 /*
  * Copyright (C) 2008 The Android Open Source Project
- * 
+ *
  * Licensed under the Eclipse Public License, Version 1.0 (the "License"); you
  * may not use this file except in compliance with the License. You may obtain a
  * copy of the License at
- * 
+ *
  * http://www.eclipse.org/org/documents/epl-v10.php
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -15,6 +15,7 @@
  */
 package com.android.ide.eclipse.tests;
 
+import com.android.ide.eclipse.tests.functests.layoutRendering.ApiDemosRenderingTest;
 import com.android.ide.eclipse.tests.functests.sampleProjects.SampleProjectTest;
 
 import junit.framework.TestSuite;
@@ -28,18 +29,19 @@ public class FuncTests extends TestSuite {
     static final String FUNC_TEST_PACKAGE = "com.android.ide.eclipse.tests.functests";
 
     public FuncTests() {
-        
+
     }
-    
+
     /**
      * Returns a suite of test cases to be run.
      * Needed for JUnit3 compliant command line test runner
      */
     public static TestSuite suite() {
         TestSuite suite = new TestSuite();
-        
+
         suite.addTestSuite(SampleProjectTest.class);
-        
+        suite.addTestSuite(ApiDemosRenderingTest.class);
+
         return suite;
     }
 
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
new file mode 100644 (file)
index 0000000..14e0080
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2009 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.tests.functests.layoutRendering;
+
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
+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.AndroidTargetParser;
+import com.android.ide.eclipse.adt.internal.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
+import com.android.ide.eclipse.tests.FuncTestCase;
+import com.android.layoutlib.api.ILayoutResult;
+import com.android.layoutlib.api.IProjectCallback;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IXmlPullParser;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+public class ApiDemosRenderingTest extends FuncTestCase {
+
+    /**
+     * Custom parser that implements {@link IXmlPullParser} (which itself extends
+     * {@link XmlPullParser}).
+     */
+    private final static class TestParser extends KXmlParser implements IXmlPullParser {
+        /**
+         * Since we're not going to go through the result of the rendering/layout, we can return
+         * null for the View Key.
+         */
+        public Object getViewKey() {
+            return null;
+        }
+    }
+
+    private final static class ProjectCallBack implements IProjectCallback {
+        // resource id counter.
+        // We start at 0x7f000000 to avoid colliding with the framework id
+        // since we have no access to the project R.java and we need to generate them automatically.
+        private int mIdCounter = 0x7f000000;
+
+        // in some cases, the id that getResourceValue(String type, String name) returns
+        // will be sent back to get the type/name. This map stores the id/type/name we generate
+        // to be able to do the reverse resolution.
+        private Map<Integer, String[]> mResourceMap = new HashMap<Integer, String[]>();
+
+        private boolean mCustomViewAttempt = false;
+
+        public String getNamespace() {
+            // TODO: read from the ApiDemos manifest.
+            return "com.example.android.apis";
+        }
+
+        public Integer getResourceValue(String type, String name) {
+            Integer result = ++mIdCounter;
+            mResourceMap.put(result, new String[] { name, type });
+            return result;
+        }
+
+        @SuppressWarnings("unchecked")
+        public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+                throws ClassNotFoundException, Exception {
+            mCustomViewAttempt = true;
+            return null;
+        }
+
+        public String[] resolveResourceValue(int id) {
+            return mResourceMap.get(id);
+        }
+
+        public String resolveResourceValue(int[] id) {
+            return null;
+        }
+
+    }
+
+    private Sdk mSdk;
+
+    public void testApiDemos() throws IOException, XmlPullParserException {
+        loadSdk();
+        findApiDemos();
+    }
+
+    /**
+     * Loads the {@link Sdk}.
+     */
+    private void loadSdk() {
+        mSdk = Sdk.loadSdk(this.getOsSdkLocation());
+
+        int n = mSdk.getTargets().length;
+        if (n > 0) {
+            for (IAndroidTarget target : mSdk.getTargets()) {
+                IStatus status = new AndroidTargetParser(target).run(new NullProgressMonitor());
+                if (status.getCode() != IStatus.OK) {
+                    fail("Failed to parse targets data");
+                }
+            }
+        }
+    }
+
+    private void findApiDemos() throws IOException, XmlPullParserException {
+        IAndroidTarget[] targets = mSdk.getTargets();
+
+        for (IAndroidTarget target : targets) {
+            String path = target.getPath(IAndroidTarget.SAMPLES);
+            File samples = new File(path);
+            if (samples.isDirectory()) {
+                File[] files = samples.listFiles();
+                for (File file : files) {
+                    if ("apidemos".equalsIgnoreCase(file.getName())) {
+                        testSample(target, file);
+                        return;
+                    }
+                }
+            }
+        }
+
+        fail("Failed to find ApiDemos!");
+    }
+
+    private void testSample(IAndroidTarget target, File sampleProject) throws IOException, XmlPullParserException {
+        AndroidTargetData data = mSdk.getTargetData(target);
+        if (data == null) {
+            fail("No AndroidData!");
+        }
+
+        LayoutBridge bridge = data.getLayoutBridge();
+        if (bridge.status != LoadStatus.LOADED || bridge.bridge == null) {
+            fail("Fail to load the bridge");
+        }
+
+        File resFolder = new File(sampleProject, SdkConstants.FD_RES);
+        if (resFolder.isDirectory() == false) {
+            fail("Sample project has no res folder!");
+        }
+
+        // look for the layout folder
+        File layoutFolder = new File(resFolder, SdkConstants.FD_LAYOUT);
+        if (layoutFolder.isDirectory() == false) {
+            fail("Sample project has no layout folder!");
+        }
+
+        // first load the project's target framework resource
+        ProjectResources framework = ResourceManager.getInstance().loadFrameworkResources(target);
+
+        // now load the project resources
+        ProjectResources project = new ProjectResources(false /* isFrameworkRepository */);
+        ResourceManager.getInstance().loadResources(project, resFolder);
+
+
+        // Create a folder configuration that will be used for the rendering:
+        FolderConfiguration config = getConfiguration();
+
+        // get the configured resources
+        Map<String, Map<String, IResourceValue>> configuredFramework =
+                framework.getConfiguredResources(config);
+        Map<String, Map<String, IResourceValue>> configuredProject =
+                project.getConfiguredResources(config);
+
+        boolean saveFiles = System.getenv("save_file") != null;
+
+        // loop on the layouts and render them
+        File[] layouts = layoutFolder.listFiles();
+        for (File layout : layouts) {
+            // create a parser for the layout file
+            TestParser parser = new TestParser();
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+            parser.setInput(new FileReader(layout));
+
+            System.out.println("Rendering " + layout.getName());
+
+            ProjectCallBack projectCallBack = new ProjectCallBack();
+
+            ILayoutResult result = bridge.bridge.computeLayout(
+                    parser,
+                    null /*projectKey*/,
+                    320,
+                    480,
+                    160, //density
+                    160, //xdpi
+                    160, // ydpi
+                    "Theme", //themeName
+                    false, //isProjectTheme
+                    configuredProject,
+                    configuredFramework,
+                    projectCallBack,
+                    null //logger
+                    );
+
+            if (result.getSuccess() != ILayoutResult.SUCCESS) {
+                if (projectCallBack.mCustomViewAttempt == false) {
+                    System.out.println("FAILED");
+                    fail(String.format("Rendering %1$s: %2$s", layout.getName(),
+                            result.getErrorMessage()));
+                } else {
+                    System.out.println("Ignore custom views for now");
+                }
+            } else {
+                if (saveFiles) {
+                    File tmp = File.createTempFile(layout.getName(), ".png");
+                    ImageIO.write(result.getImage(), "png", tmp);
+                }
+                System.out.println("Success!");
+            }
+        }
+    }
+
+    private FolderConfiguration getConfiguration() {
+        FolderConfiguration config = new FolderConfiguration();
+
+        return config;
+    }
+}
index dd82bed..7601648 100644 (file)
@@ -34,6 +34,7 @@ import com.android.ide.eclipse.adt.internal.resources.manager.files.IFileWrapper
 import com.android.ide.eclipse.adt.internal.resources.manager.files.IFolderWrapper;
 import com.android.ide.eclipse.mock.FileMock;
 import com.android.ide.eclipse.mock.FolderMock;
+import com.android.sdklib.IAndroidTarget;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -44,7 +45,7 @@ public class ConfigMatchTest extends TestCase {
     private static final String SEARCHED_FILENAME = "main.xml"; //$NON-NLS-1$
     private static final String MISC1_FILENAME = "foo.xml"; //$NON-NLS-1$
     private static final String MISC2_FILENAME = "bar.xml"; //$NON-NLS-1$
-    
+
     private ProjectResources mResources;
     private ResourceQualifier[] mQualifierList;
     private FolderConfiguration config4;
@@ -55,20 +56,20 @@ public class ConfigMatchTest extends TestCase {
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        
+
         // create a Resource Manager to get a list of qualifier as instantiated by the real code.
-        // Thanks for QualifierListTest we know this contains all the qualifiers. 
+        // Thanks for QualifierListTest we know this contains all the qualifiers.
         ResourceManager manager = ResourceManager.getInstance();
         Field qualifierListField = ResourceManager.class.getDeclaredField("mQualifiers");
         assertNotNull(qualifierListField);
         qualifierListField.setAccessible(true);
-        
+
         // get the actual list.
         mQualifierList = (ResourceQualifier[])qualifierListField.get(manager);
-        
+
         // create the project resources.
         mResources = new ProjectResources(false /* isFrameworkRepository */);
-        
+
         // create 2 arrays of IResource. one with the filename being looked up, and one without.
         // Since the required API uses IResource, we can use MockFolder for them.
         FileMock[] validMemberList = new FileMock[] {
@@ -80,7 +81,7 @@ public class ConfigMatchTest extends TestCase {
                 new FileMock(MISC1_FILENAME),
                 new FileMock(MISC2_FILENAME),
         };
-        
+
         // add multiple ResourceFolder to the project resource.
         FolderConfiguration defaultConfig = getConfiguration(
                 null, // country code
@@ -94,9 +95,9 @@ public class ConfigMatchTest extends TestCase {
                 null, // text input
                 null, // navigation
                 null); // screen size
-        
+
         addFolder(mResources, defaultConfig, validMemberList);
-        
+
         config1 = getConfiguration(
                 null, // country code
                 null, // network code
@@ -109,7 +110,7 @@ public class ConfigMatchTest extends TestCase {
                 null, // text input
                 null, // navigation
                 null); // screen size
-        
+
         addFolder(mResources, config1, validMemberList);
 
         config2 = getConfiguration(
@@ -124,7 +125,7 @@ public class ConfigMatchTest extends TestCase {
                 null, // text input
                 null, // navigation
                 null); // screen size
-        
+
         addFolder(mResources, config2, validMemberList);
 
         config3 = getConfiguration(
@@ -139,7 +140,7 @@ public class ConfigMatchTest extends TestCase {
                 null, // text input
                 null, // navigation
                 null); // screen size
-        
+
         addFolder(mResources, config3, validMemberList);
 
         config4 = getConfiguration(
@@ -154,7 +155,7 @@ public class ConfigMatchTest extends TestCase {
                 TextInputMethod.QWERTY.getValue(), // text input
                 NavigationMethod.DPAD.getValue(), // navigation
                 "480x320"); // screen size
-        
+
         addFolder(mResources, config4, invalidMemberList);
     }
 
@@ -177,10 +178,10 @@ public class ConfigMatchTest extends TestCase {
                 TextInputMethod.QWERTY.getValue(), // text input
                 NavigationMethod.DPAD.getValue(), // navigation
                 "480x320"); // screen size
-        
+
         ResourceFile result = mResources.getMatchingFile(SEARCHED_FILENAME,
                 ResourceFolderType.LAYOUT, testConfig);
-        
+
         boolean bresult = result.getFolder().getConfiguration().equals(config3);
         assertEquals(bresult, true);
     }
@@ -193,10 +194,10 @@ public class ConfigMatchTest extends TestCase {
      */
     private FolderConfiguration getConfiguration(String... qualifierValues) {
         FolderConfiguration config = new FolderConfiguration();
-        
+
         // those must be of the same length
         assertEquals(qualifierValues.length, mQualifierList.length);
-        
+
         int index = 0;
 
         for (ResourceQualifier qualifier : mQualifierList) {
@@ -208,7 +209,7 @@ public class ConfigMatchTest extends TestCase {
 
         return config;
     }
-    
+
     /**
      * Adds a folder to the given {@link ProjectResources} with the given
      * {@link FolderConfiguration}. The folder is filled with files from the provided list.
@@ -218,13 +219,10 @@ public class ConfigMatchTest extends TestCase {
      */
     private void addFolder(ProjectResources resources, FolderConfiguration config,
             FileMock[] memberList) throws Exception {
-        
+
         // figure out the folder name based on the configuration
-        String folderName = "layout";
-        if (config.isDefault() == false) {
-            folderName += "-" + config.toString();
-        }
-        
+        String folderName = config.getFolderName(ResourceFolderType.LAYOUT, (IAndroidTarget)null);
+
         // create the folder mock
         FolderMock folder = new FolderMock(folderName, memberList);
 
@@ -237,15 +235,15 @@ public class ConfigMatchTest extends TestCase {
         }
     }
 
-    /** Calls ProjectResource.add method via reflection to circumvent access 
-     * restrictions that are enforced when running in the plug-in environment 
+    /** Calls ProjectResource.add method via reflection to circumvent access
+     * restrictions that are enforced when running in the plug-in environment
      * ie cannot access package or protected members in a different plug-in, even
      * if they are in the same declared package as the accessor
      */
     private ResourceFolder _addProjectResourceFolder(ProjectResources resources,
             FolderConfiguration config, FolderMock folder) throws Exception {
 
-        Method addMethod = ProjectResources.class.getDeclaredMethod("add", 
+        Method addMethod = ProjectResources.class.getDeclaredMethod("add",
                 ResourceFolderType.class, FolderConfiguration.class,
                 IAbstractFolder.class);
         addMethod.setAccessible(true);
@@ -253,7 +251,4 @@ public class ConfigMatchTest extends TestCase {
                 ResourceFolderType.LAYOUT, config, new IFolderWrapper(folder));
         return resFolder;
     }
-    
-
-    
 }
index 7dee775..a00d1a4 100644 (file)
@@ -3,10 +3,10 @@
    <description url="https://dl-ssl.google.com/android/eclipse/">
       Update Site for Android Development Toolkit
    </description>
-   <feature url="features/com.android.ide.eclipse.adt_0.9.2.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.2.qualifier">
+   <feature url="features/com.android.ide.eclipse.adt_0.9.3.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.3.qualifier">
       <category name="developer"/>
    </feature>
-   <feature url="features/com.android.ide.eclipse.ddms_0.9.2.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.2.qualifier">
+   <feature url="features/com.android.ide.eclipse.ddms_0.9.3.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.3.qualifier">
       <category name="developer"/>
    </feature>
    <category-def name="developer" label="Developer Tools">
index 4fd5333..e19c119 100644 (file)
@@ -3,13 +3,13 @@
    <description url="https://android.corp.google.com/adt/">
       Update Site for Android Development Toolkit
    </description>
-   <feature url="features/com.android.ide.eclipse.adt_0.9.2.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.2.qualifier">
+   <feature url="features/com.android.ide.eclipse.adt_0.9.3.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.3.qualifier">
       <category name="developer"/>
    </feature>
-   <feature url="features/com.android.ide.eclipse.ddms_0.9.2.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.2.qualifier">
+   <feature url="features/com.android.ide.eclipse.ddms_0.9.3.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.3.qualifier">
       <category name="developer"/>
    </feature>
-   <feature url="features/com.android.ide.eclipse.tests_0.9.2.qualifier.jar" id="com.android.ide.eclipse.tests" version="0.9.2.qualifier">
+   <feature url="features/com.android.ide.eclipse.tests_0.9.3.qualifier.jar" id="com.android.ide.eclipse.tests" version="0.9.3.qualifier">
       <category name="test"/>
    </feature>
    <category-def name="developer" label="Application Developer Tools">
diff --git a/tools/monkeyrunner/Android.mk b/tools/monkeyrunner/Android.mk
new file mode 100644 (file)
index 0000000..d15c67e
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+MONKEYRUNNER_LOCAL_DIR := $(call my-dir)
+include $(MONKEYRUNNER_LOCAL_DIR)/etc/Android.mk
+include $(MONKEYRUNNER_LOCAL_DIR)/src/Android.mk
diff --git a/tools/monkeyrunner/MODULE_LICENSE_APACHE2 b/tools/monkeyrunner/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tools/monkeyrunner/NOTICE b/tools/monkeyrunner/NOTICE
new file mode 100644 (file)
index 0000000..c5b1efa
--- /dev/null
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/tools/monkeyrunner/etc/Android.mk b/tools/monkeyrunner/etc/Android.mk
new file mode 100644 (file)
index 0000000..2d757fd
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PREBUILT_EXECUTABLES := monkeyrunner
+include $(BUILD_HOST_PREBUILT)
diff --git a/tools/monkeyrunner/etc/manifest.txt b/tools/monkeyrunner/etc/manifest.txt
new file mode 100644 (file)
index 0000000..288be5f
--- /dev/null
@@ -0,0 +1 @@
+Main-Class: com.android.monkeyrunner.MonkeyRunner
diff --git a/tools/monkeyrunner/etc/monkeyrunner b/tools/monkeyrunner/etc/monkeyrunner
new file mode 100755 (executable)
index 0000000..364be2a
--- /dev/null
@@ -0,0 +1,74 @@
+#!/bin/sh
+# Copyright 2005-2007, 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=monkeyrunner.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
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+    jarpath=`cygpath -w  "$frameworkdir/$jarfile"`
+    progdir=`cygpath -w  "$progdir"`
+else
+    jarpath="$frameworkdir/$jarfile"
+fi
+
+# need to use "java.ext.dirs" because "-jar" causes classpath to be ignored
+# might need more memory, e.g. -Xmx128M
+exec java -Xmx128M $os_opts $java_debug -Djava.ext.dirs="$frameworkdir" -Djava.library.path="$libdir" -Dcom.android.monkeyrunner.bindir="$progdir" -jar "$jarpath" "$@"
diff --git a/tools/monkeyrunner/src/Android.mk b/tools/monkeyrunner/src/Android.mk
new file mode 100644 (file)
index 0000000..576025a
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+LOCAL_JAVA_LIBRARIES := \
+       ddmlib \
+       jython
+
+
+LOCAL_MODULE := monkeyrunner
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
new file mode 100644 (file)
index 0000000..cbc881c
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.monkeyrunner;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.NullOutputReceiver;
+import com.android.ddmlib.RawImage;
+import com.android.ddmlib.Log.ILogOutput;
+import com.android.ddmlib.Log.LogLevel;
+
+import java.awt.image.BufferedImage;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import javax.imageio.ImageIO;
+
+/**
+ *  MonkeyRunner is a host side application to control a monkey instance on a
+ * device. MonkeyRunner provides some useful helper functions to control the
+ * device as well as various other methods to help script tests. 
+ */
+public class MonkeyRunner {
+
+  static String monkeyServer = "127.0.0.1";
+  static int monkeyPort = 1080;
+  static Socket monkeySocket = null;
+
+  static IDevice monkeyDevice;
+  
+  static BufferedReader monkeyReader;
+  static BufferedWriter monkeyWriter;
+
+  static String scriptName = null;
+
+  // delay between key events
+  final static int KEY_INPUT_DELAY = 1000;
+
+  public static void main(String[] args) {
+
+    processOptions(args);
+    
+    initAdbConnection();
+    openMonkeyConnection();
+
+    start_script();
+    
+    ScriptRunner.run(scriptName);
+   
+    end_script();
+    closeMonkeyConnection();  
+  }
+
+  /**
+   *  Initialize an adb session with a device connected to the host
+   * 
+   */
+  public static void initAdbConnection() {
+    String adbLocation = "adb";
+    boolean device = false;
+    boolean emulator = false;
+    String serial = null;
+
+    AndroidDebugBridge.init(false /* debugger support */);
+
+    try {
+      AndroidDebugBridge bridge = AndroidDebugBridge.createBridge(
+          adbLocation, true /* forceNewBridge */);
+
+      // we can't just ask for the device list right away, as the internal thread getting
+      // them from ADB may not be done getting the first list.
+      // Since we don't really want getDevices() to be blocking, we wait here manually.
+      int count = 0;
+      while (bridge.hasInitialDeviceList() == false) {
+        try {
+          Thread.sleep(100);
+          count++;
+        } catch (InterruptedException e) {
+          // pass
+        }
+
+        // let's not wait > 10 sec.
+        if (count > 100) {
+          System.err.println("Timeout getting device list!");
+          return;
+        }
+      }
+
+      // now get the devices
+      IDevice[] devices = bridge.getDevices();
+
+      if (devices.length == 0) {
+        printAndExit("No devices found!", true /* terminate */);
+      }
+
+      monkeyDevice = null;
+
+      if (emulator || device) {
+        for (IDevice d : devices) {
+          // this test works because emulator and device can't both be true at the same
+          // time.
+          if (d.isEmulator() == emulator) {
+            // if we already found a valid target, we print an error and return.
+            if (monkeyDevice != null) {
+              if (emulator) {
+                printAndExit("Error: more than one emulator launched!",
+                    true /* terminate */);
+              } else {
+                printAndExit("Error: more than one device connected!",true /* terminate */);
+              }
+            }
+            monkeyDevice = d;
+          }
+        }
+      } else if (serial != null) {
+        for (IDevice d : devices) {
+          if (serial.equals(d.getSerialNumber())) {
+            monkeyDevice = d;
+            break;
+          }
+        }
+      } else {
+        if (devices.length > 1) {
+          printAndExit("Error: more than one emulator or device available!",
+              true /* terminate */);
+        }
+        monkeyDevice = devices[0];
+      }
+
+      monkeyDevice.createForward(monkeyPort, monkeyPort);
+      String command = "monkey --port " + monkeyPort;
+      monkeyDevice.executeShellCommand(command, new NullOutputReceiver());
+
+    } catch(IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Open a tcp session over adb with the device to communicate monkey commands
+   */
+  public static void openMonkeyConnection() {
+    try {
+      InetAddress addr = InetAddress.getByName(monkeyServer);
+      monkeySocket = new Socket(addr, monkeyPort);
+    } catch (UnknownHostException e) {
+      e.printStackTrace();
+    } catch(IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /** 
+   * Close tcp session with the monkey on the device
+   * 
+   */
+  public static void closeMonkeyConnection() {
+    try {
+      monkeyReader.close();
+      monkeyWriter.close();
+      monkeySocket.close();
+      AndroidDebugBridge.terminate();
+    } catch(IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /** 
+   * This is a house cleaning type routine to run before starting a script. Puts
+   * the device in a known state.
+   */
+  public static void start_script() {
+    press("menu");
+    press("menu");
+    press("home");
+  }
+
+  public static void end_script() {
+    String command = "END";
+    sendMonkeyEvent(command);
+  }
+
+  /** This is a method for scripts to launch an activity on the device
+   * 
+   * @param name The name of the activity to launch 
+   */
+  public static void launch_activity(String name) {
+    try {
+      System.out.println("Launching: " + name);
+      monkeyDevice.executeShellCommand("am start -a android.intent.action.MAIN -n " 
+          + name, new NullOutputReceiver());
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Grabs the current state of the screen stores it as a png
+   * 
+   * @param tag filename or tag descriptor of the screenshot
+   */
+  public static void grabscreen(String tag) {
+    tag += ".png";
+
+    try {
+      Thread.sleep(1000);
+      getDeviceImage(monkeyDevice, tag, false);
+    } catch (IOException e) {
+      e.printStackTrace();
+    } catch (InterruptedException e) {
+    }
+  }
+
+  /**
+   * Sleeper method for script to call
+   * 
+   * @param msec msecs to sleep for
+   */
+  public static void sleep(int msec) {
+    try {
+      Thread.sleep(msec);
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Tap function for scripts to call at a particular x and y location
+   * 
+   * @param x x-coordinate
+   * @param y y-coordinate
+   */
+  public static boolean tap(int x, int y) {
+    String command = "touch down " + x + "  " + y + "\r\n" +
+    "touch up " + x + " " + y + "\r\n";
+
+    System.out.println("Tapping: " + x + ", " + y);
+    return sendMonkeyEvent(command);
+  }
+
+  /** 
+   * Press function for scripts to call on a particular button or key
+   * 
+   * @param key key to press
+   */
+  public static boolean press(String key) {
+    String command = "key down " + key + "\r\n" +
+    "key up " + key + "\r\n";
+
+    System.out.println("Pressing: " + key);
+    return sendMonkeyEvent(command);
+  }
+
+  /**
+   * dpad down function
+   */
+  public static boolean down() {
+    return press("dpad_down");
+  }
+
+  /**
+   * dpad up function
+   */
+  public static boolean up() {
+    return press("dpad_up");
+  }
+
+  /**
+   * Function to type text on the device
+   * 
+   * @param text text to type
+   */
+  public static boolean type(String text) {
+    System.out.println("Typing: " + text);
+    for (int i=0; i<text.length(); i++) {
+      String command = "key down ";
+      char c = text.charAt(i);
+      if(Character.isDigit(c)) {
+        command += "KEYCODE_" + c + "\n" + "key up KEYCODE_" + c + "\n";
+      } else {
+        command = "key down " + c + "\n" + "key up " + c + "\n";
+      }
+
+      if(!sendMonkeyEvent(command)) {
+        System.out.println("\nERROR: Key not set \n");
+      }
+
+      // lets delay a bit after each input to ensure accuracy
+      try {
+        Thread.sleep(KEY_INPUT_DELAY);
+      } catch (InterruptedException e) {
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * This function is the communication bridge between the host and the device.
+   * It sends monkey events and waits for responses over the adb tcp socket.
+   * 
+   * @param command the monkey command to send to the device
+   */
+  private static boolean sendMonkeyEvent(String command) {
+    try {
+      monkeyWriter = new BufferedWriter(
+          new OutputStreamWriter(monkeySocket.getOutputStream()));
+      monkeyWriter.write(command);
+      monkeyWriter.flush();
+
+      monkeyReader = new BufferedReader(
+          new InputStreamReader(monkeySocket.getInputStream()));
+      String response = monkeyReader.readLine();
+
+      sleep(1000);
+      System.out.println("MonkeyServer: " + response);
+      if(response.equals("OK"))
+        return true;
+      if(response.equals("ERROR"))
+        return false;
+
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    return false;
+  }
+
+
+  /**
+   * Process the command-line options
+   *
+   * @return Returns true if options were parsed with no apparent errors.
+   */
+  public static void processOptions(String[] args) {
+    // parse command line parameters.
+    int index = 0;
+
+    do {
+      String argument = args[index++];
+
+      if ("-s".equals(argument)) {
+        if(index == args.length) {
+          printAndExit("Missing Server after -s", false);
+        }
+
+        monkeyServer = args[index++];
+
+      } else if ("-p".equals(argument)) {
+        // quick check on the next argument.
+        if (index == args.length) {
+          printAndExit("Missing Server IP after -p", false /* terminate */);
+        }
+
+        monkeyPort = Integer.parseInt(args[index++]);
+      } else {
+        // get the filepath of the script to run.
+        scriptName = argument;
+
+        // should not be any other device.
+        //if (index < args.length) {
+        //    printAndExit("Too many arguments!", false /* terminate */);
+        //}
+      }
+    } while (index < args.length);
+  }
+
+  /*
+   * Grab an image from an ADB-connected device.
+   */
+  private static void getDeviceImage(IDevice device, String filepath, boolean landscape)
+  throws IOException {
+    RawImage rawImage;
+
+    try {
+      rawImage = device.getScreenshot();
+    }
+    catch (IOException ioe) {
+      printAndExit("Unable to get frame buffer: " + ioe.getMessage(), true /* terminate */);
+      return;
+    }
+
+    // device/adb not available?
+    if (rawImage == null)
+      return;
+
+    assert rawImage.bpp == 16;
+
+    BufferedImage image;
+
+    if (landscape) {
+      // convert raw data to an Image
+      image = new BufferedImage(rawImage.height, rawImage.width,
+          BufferedImage.TYPE_INT_ARGB);
+
+      byte[] buffer = rawImage.data;
+      int index = 0;
+      for (int y = 0 ; y < rawImage.height ; y++) {
+        for (int x = 0 ; x < rawImage.width ; x++) {
+
+          int value = buffer[index++] & 0x00FF;
+          value |= (buffer[index++] << 8) & 0x0FF00;
+
+          int r = ((value >> 11) & 0x01F) << 3;
+          int g = ((value >> 5) & 0x03F) << 2;
+          int b = ((value >> 0) & 0x01F) << 3;
+
+          value = 0xFF << 24 | r << 16 | g << 8 | b;
+
+          image.setRGB(y, rawImage.width - x - 1, value);
+        }
+      }
+    } else {
+      // convert raw data to an Image
+      image = new BufferedImage(rawImage.width, rawImage.height,
+          BufferedImage.TYPE_INT_ARGB);
+
+      byte[] buffer = rawImage.data;
+      int index = 0;
+      for (int y = 0 ; y < rawImage.height ; y++) {
+        for (int x = 0 ; x < rawImage.width ; x++) {
+
+          int value = buffer[index++] & 0x00FF;
+          value |= (buffer[index++] << 8) & 0x0FF00;
+
+          int r = ((value >> 11) & 0x01F) << 3;
+          int g = ((value >> 5) & 0x03F) << 2;
+          int b = ((value >> 0) & 0x01F) << 3;
+
+          value = 0xFF << 24 | r << 16 | g << 8 | b;
+
+          image.setRGB(x, y, value);
+        }
+      }
+    }
+
+    if (!ImageIO.write(image, "png", new File(filepath))) {
+      throw new IOException("Failed to find png writer");
+    }
+  }
+
+  private static void printUsageAndQuit() {
+    // 80 cols marker:  01234567890123456789012345678901234567890123456789012345678901234567890123456789
+    System.out.println("Usage: monkeyrunner [options] SCRIPT_FILE");
+    System.out.println("");
+    System.out.println("    -s      MonkeyServer IP Address.");
+    System.out.println("    -p      MonkeyServer TCP Port.");
+    System.out.println("");
+    System.out.println("");
+
+    System.exit(1);
+  }
+
+  private static void printAndExit(String message, boolean terminate) {
+    System.out.println(message);
+    if (terminate) {
+      AndroidDebugBridge.terminate();
+    }
+    System.exit(1);
+  }
+}
diff --git a/tools/monkeyrunner/src/com/android/monkeyrunner/ScriptRunner.java b/tools/monkeyrunner/src/com/android/monkeyrunner/ScriptRunner.java
new file mode 100644 (file)
index 0000000..6a4405b
--- /dev/null
@@ -0,0 +1,96 @@
+package com.android.monkeyrunner;
+
+import org.python.core.Py;
+import org.python.core.PyObject;
+import org.python.util.PythonInterpreter;
+import org.python.util.InteractiveConsole;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.lang.RuntimeException;
+import java.util.Properties;
+
+
+/**
+ * Runs Jython based scripts.
+ */
+public class ScriptRunner {
+
+  /** The "this" scope object for scripts. */
+  private final Object scope;
+  private final String variable;
+  
+  /** Private constructor. */
+  private ScriptRunner(Object scope, String variable) {
+    this.scope = scope;
+    this.variable = variable;
+  }
+  
+  /** Creates a new instance for the given scope object. */
+  public static ScriptRunner newInstance(Object scope, String variable) {
+    return new ScriptRunner(scope, variable);
+  }
+
+  /**
+   * Runs the specified Jython script. First runs the initialization script to
+   * preload the appropriate client library version.
+   */
+  public static void run(String scriptfilename) {
+    try {
+      initPython();
+      PythonInterpreter python = new PythonInterpreter();
+      
+      python.execfile(scriptfilename);
+    } catch(Exception e) {
+      e.printStackTrace();
+    }
+  }
+  
+
+  /** Initialize the python interpreter. */
+  private static void initPython() {
+    Properties props = new Properties();
+    // Default is 'message' which displays sys-package-mgr bloat
+    // Choose one of error,warning,message,comment,debug
+    props.setProperty("python.verbose", "error");
+    props.setProperty("python.path", System.getProperty("java.class.path"));
+    PythonInterpreter.initialize(System.getProperties(), props, new String[] {""});
+  }
+
+  /**
+   * Create and run a console using a new python interpreter for the test
+   * associated with this instance.
+   */
+  public void console() throws IOException {
+    initPython();
+    InteractiveConsole python = new InteractiveConsole();
+    initInterpreter(python, scope, variable);
+    python.interact();
+  }
+
+  /**
+   * Start an interactive python interpreter using the specified set of local
+   * variables. Use this to interrupt a running test script with a prompt:
+   * 
+   * @param locals
+   */
+  public static void console(PyObject locals) {
+    initPython();
+    InteractiveConsole python = new InteractiveConsole(locals);
+    python.interact();
+  }
+
+  /**
+   * Initialize a python interpreter.
+   * 
+   * @param python
+   * @param scope
+   * @throws IOException
+   */
+  public static void initInterpreter(PythonInterpreter python, Object scope, String variable) 
+      throws IOException {
+    // Store the current test case as the this variable
+    python.set(variable, scope);
+  }
+}
index b1be4c8..6606d3d 100644 (file)
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--
+ALIAS PROJECTS ARE CURRENT NOT SUPPORTED.
+THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
+-->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="PACKAGE"
       android:versionCode="1"
index 8be355b..14899ce 100644 (file)
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--
+ALIAS PROJECTS ARE CURRENT NOT SUPPORTED.
+THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
+-->
 <alias xmlns:android="http://schemas.android.com/apk/res/android">
     <intent android:action="android.intent.action.VIEW"
         android:data="ALIASDATA"/>
index bc7de7c..5702461 100644 (file)
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+ALIAS PROJECTS ARE CURRENT NOT SUPPORTED.
+THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
+-->
 <project name="alias_rules" default="package">
 
     <!-- No user servicable parts below. -->
index 1f5daac..ad36cbe 100644 (file)
     </condition>
 
     <!-- The final package file to generate -->
+    <property name="out-debug-unaligned-package" value="${out-folder}/${ant.project.name}-debug-unaligned.apk"/>
     <property name="out-debug-package" value="${out-folder}/${ant.project.name}-debug.apk"/>
+    <property name="out-unsigned-package" value="${out-folder}/${ant.project.name}-unsigned.apk"/>
+    <property name="out-unaligned-package" value="${out-folder}/${ant.project.name}-unaligned.apk"/>
+    <property name="out-release-package" value="${out-folder}/${ant.project.name}-release.apk"/>
 
     <!-- Tools -->
     <condition property="exe" value=".exe" else=""><os family="windows"/></condition>
     <property name="adb" value="${android-tools}/adb${exe}"/>
+    <property name="zipalign" value="${android-tools}/zipalign${exe}" />
 
     <!-- rules -->
 
                 basename="${ant.project.name}" />
     </target>
 
-    <!-- Package the application and sign it with a debug key.
-         This is the default target when building. It is used for debug. -->
-    <target name="debug" depends="dex, package-resources">
+    <!-- Package the application and (maybe) sign it with a debug key.
+         This requires the property sign.package to be set to true or false. -->
+    <target name="package">
         <apkbuilder
                 outfolder="${out-folder}"
                 basename="${ant.project.name}"
-                signed="true"
-                verbose="false">
+                signed="${sign.package}"
+                verbose="true">
             <file path="${intermediate-dex}" />
             <sourcefolder path="${source-folder}" />
             <jarfolder path="${external-libs-folder}" />
         </apkbuilder>
     </target>
 
-    <!-- Package the application without signing it.
-         This allows for the application to be signed later with an official publishing key. -->
-    <target name="release" depends="dex, package-resources">
-        <apkbuilder
-                outfolder="${out-folder}"
-                basename="${ant.project.name}"
-                signed="false"
-                verbose="false">
-            <file path="${intermediate-dex}" />
-            <sourcefolder path="${source-folder}" />
-            <jarfolder path="${external-libs-folder}" />
-            <nativefolder path="${native-libs-folder}" />
-        </apkbuilder>
-        <echo>All generated packages need to be signed with jarsigner before they are published.</echo>
+    <target name="no-sign">
+       <property name="sign.package" value="false" />
     </target>
 
-    <!-- Install the package on the default emulator -->
-    <target name="install" depends="debug">
-        <echo>Installing ${out-debug-package} onto default emulator...</echo>
-        <exec executable="${adb}" failonerror="true">
-            <arg value="install" />
+    <target name="debug-sign">
+       <property name="sign.package" value="true" />
+    </target>
+
+    <target name="debug" depends="dex, package-resources, debug-sign, package">
+        <echo>Running zip align on final apk...</echo>
+        <exec executable="${zipalign}" failonerror="true">
+            <arg value="-f" />
+            <arg value="4" />
+            <arg path="${out-debug-unaligned-package}" />
             <arg path="${out-debug-package}" />
         </exec>
+        <echo>Debug Package: ${out-debug-package}</echo>
+    </target>
+
+    <target name="release-package" depends="dex, package-resources, no-sign, package">
     </target>
 
-    <target name="reinstall" depends="debug">
+    <target name="release.check">
+        <condition property="release.sign">
+            <and>
+                <isset property="key.store" />
+                <isset property="key.alias" />
+            </and>
+        </condition>
+    </target>
+    <target name="release.nosign" depends="release.check" unless="release.sign">
+        <echo>No key.store and key.alias properties found in build.properties.</echo>
+        <echo>Please sign ${out-unsigned-package} manually</echo>
+        <echo>and run zipalign from the Android SDK tools.</echo>
+    </target>
+
+    <target name="release" depends="release-package, release.nosign" if="release.sign">
+        <!-- get passwords -->
+        <input
+                message="Please enter keystore password (store:${key.store}):"
+                addproperty="key.store.password"/>
+        <input
+                message="Please enter password for alias '${key.alias}':"
+                addproperty="key.alias.password"/>
+        <!-- sign the APK -->
+        <echo>Signing final apk...</echo>
+        <signjar
+                jar="${out-unsigned-package}"
+                signedjar="${out-unaligned-package}"
+                keystore="${key.store}"
+                storepass="${key.store.password}"
+                alias="${key.alias}"
+                keypass="${key.alias.password}"/>
+        <!-- zip align the APK -->
+        <echo>Running zip align on final apk...</echo>
+        <exec executable="${zipalign}" failonerror="true">
+            <arg value="-f" />
+            <arg value="4" />
+            <arg path="${out-unaligned-package}" />
+            <arg path="${out-release-package}" />
+        </exec>
+        <echo>Release Package: ${out-release-package}</echo>
+    </target>
+
+    <!-- Install the package on the default emulator -->
+    <target name="install" depends="debug">
         <echo>Installing ${out-debug-package} onto default emulator...</echo>
         <exec executable="${adb}" failonerror="true">
             <arg value="install" />
     </target>
 
     <!-- Uinstall the package from the default emulator -->
-    <target name="uninstall">
+    <target name="uninstall.check">
+        <condition property="uninstall.run">
+           <isset property="application-package" />
+        </condition>
+    </target>
+    <target name="uninstall.error" depends="uninstall.check" unless="uninstall.run">
+        <echo>Unable to run 'ant unintall', application-package is not defined in build.properties</echo>
+    </target>
+    <target name="uninstall" depends="uninstall.error" if="uninstall.run">
         <echo>Uninstalling ${application-package} from the default emulator...</echo>
         <exec executable="${adb}" failonerror="true">
             <arg value="uninstall" />
         <echo>   debug:     Builds the application and sign it with a debug key.</echo>
         <echo>   release:   Builds the application. The generated apk file must be</echo>
         <echo>              signed before it is published.</echo>
-        <echo>   install:   Installs the debug package onto a running emulator or</echo>
-        <echo>              device. This can only be used if the application has </echo>
-        <echo>              not yet been installed.</echo>
-        <echo>   reinstall: Installs the debug package on a running emulator or</echo>
-        <echo>              device that already has the application.</echo>
-        <echo>              The signatures must match.</echo>
+        <echo>   install:   Installs/reinstall the debug package onto a running</echo>
+        <echo>              emulator or device.</echo>
+        <echo>              If the application was previously installed, the</echo>
+        <echo>              signatures must match.</echo>
         <echo>   uninstall: uninstall the application from a running emulator or</echo>
         <echo>              device.</echo>
     </target>
index b605295..1043af1 100644 (file)
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+ALIAS PROJECTS ARE CURRENT NOT SUPPORTED.
+THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
+-->
 <project name="PROJECT_NAME" default="package">
 
     <!-- The build.properties file can be created by you and is never touched
index 7939e6c..1ed3853 100644 (file)
         classpathref="android.antlibs"/>
 
     <!-- Execute the Android Setup task that will setup some properties specific to the target,
-         and import the rules files.
-         To customize the rules, copy/paste them below the task, and disable import by setting
-         the import attribute to false:
-            <setup import="false" />
+         and import the build rules files.
+
+         The rules file is imported from
+            <SDK>/platforms/<target_platform>/templates/android_rules.xml
+
+         To customize some build steps for your project:
+         - copy the content of the main node <project> from android_rules.xml
+         - paste it in this build.xml below the <setup /> task.
+         - disable the import by changing the setup task below to <setup import="false" />
          
          This will ensure that the properties are setup correctly but that your customized
-         targets are used.
+         build steps are used.
     -->
     <setup />
 </project>
index f4cb994..9382836 100644 (file)
@@ -1,5 +1,4 @@
 Pkg.UserSrc=false
-Platform.Version=Donut
+Platform.Version=1.6
 Pkg.Revision=1
-AndroidVersion.CodeName=Donut
-AndroidVersion.ApiLevel=3
+AndroidVersion.ApiLevel=4
index 7781a33..c6fa873 100644 (file)
@@ -1,6 +1,6 @@
 package PACKAGE;
 
-import android.test.ActivityInstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
 
 /**
  * This is a simple framework for a test of an Application.  See
@@ -12,7 +12,7 @@ import android.test.ActivityInstrumentationTestCase;
  * -e class PACKAGE.ACTIVITY_NAMETest \
  * PACKAGE.tests/android.test.InstrumentationTestRunner
  */
-public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase<ACTIVITY_NAME> {
+public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase2<ACTIVITY_NAME> {
 
     public ACTIVITY_NAMETest() {
         super("PACKAGE", ACTIVITY_NAME.class);
index 82de162..49dc3ae 100644 (file)
@@ -1,6 +1,5 @@
-Pkg.Desc=Android SDK Platform Donut_r1
+Pkg.Desc=Android SDK Platform 1.6_r1
 Pkg.UserSrc=false
-Platform.Version=Donut
+Platform.Version=1.6
 Pkg.Revision=1
-AndroidVersion.CodeName=Donut
-AndroidVersion.ApiLevel=3
+AndroidVersion.ApiLevel=4
index b19ce0a..032985d 100755 (executable)
@@ -43,9 +43,10 @@ if not "%1"=="" goto EndTempCopy
 
     set tmpdir=%TEMP%\temp-android-tool
     xcopy lib\x86 %tmpdir%\lib\x86 /I /E /C /G /R /O /Y /Q > nul
-    copy /B /D /Y lib\androidprefs.jar %tmpdir%\lib\       > nul
-    copy /B /D /Y lib\org.eclipse.*    %tmpdir%\lib\       > nul
-    copy /B /D /Y lib\sdk*             %tmpdir%\lib\       > nul
+    copy /B /D /Y lib\androidprefs.jar   %tmpdir%\lib\       > nul
+    copy /B /D /Y lib\org.eclipse.*      %tmpdir%\lib\       > nul
+    copy /B /D /Y lib\sdk*               %tmpdir%\lib\       > nul
+    copy /B /D /Y lib\commons-compress*  %tmpdir%\lib\       > nul
 
     rem jarpath and swt_path are relative to PWD so we don't need to adjust them, just change dirs.
     set toolsdir=%cd%
index 8ab1364..c800054 100755 (executable)
@@ -48,6 +48,7 @@ public class SettingsPage extends Composite implements ISettingsPage {
     private Text mProxyServerText;\r
     private Text mProxyPortText;\r
     private Button mForceHttpCheck;\r
+    private Button mAskAdbRestartCheck;\r
 \r
     private ModifyListener mSetApplyDirty = new ModifyListener() {\r
         public void modifyText(ModifyEvent e) {\r
@@ -73,18 +74,26 @@ public class SettingsPage extends Composite implements ISettingsPage {
         mProxyServerLabel = new Label(mProxySettingsGroup, SWT.NONE);\r
         mProxyServerLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
         mProxyServerLabel.setText("HTTP Proxy Server");\r
+        String tooltip = "The DNS name or IP of the HTTP proxy server to use. " +\r
+                         "When empty, no HTTP proxy is used.";\r
+        mProxyServerLabel.setToolTipText(tooltip);\r
 \r
         mProxyServerText = new Text(mProxySettingsGroup, SWT.BORDER);\r
         mProxyServerText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));\r
         mProxyServerText.addModifyListener(mSetApplyDirty);\r
+        mProxyServerText.setToolTipText(tooltip);\r
 \r
         mProxyPortLabel = new Label(mProxySettingsGroup, SWT.NONE);\r
         mProxyPortLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
         mProxyPortLabel.setText("HTTP Proxy Port");\r
+        tooltip = "The port of the HTTP proxy server to use. " +\r
+                  "When empty, the default for HTTP or HTTPS is used.";\r
+        mProxyPortLabel.setToolTipText(tooltip);\r
 \r
         mProxyPortText = new Text(mProxySettingsGroup, SWT.BORDER);\r
         mProxyPortText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));\r
         mProxyPortText.addModifyListener(mSetApplyDirty);\r
+        mProxyPortText.setToolTipText(tooltip);\r
 \r
         mMiscGroup = new Group(this, SWT.NONE);\r
         mMiscGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));\r
@@ -92,7 +101,10 @@ public class SettingsPage extends Composite implements ISettingsPage {
         mMiscGroup.setLayout(new GridLayout(2, false));\r
 \r
         mForceHttpCheck = new Button(mMiscGroup, SWT.CHECK);\r
+        mForceHttpCheck.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));\r
         mForceHttpCheck.setText("Force https://... sources to be fetched using http://...");\r
+        mForceHttpCheck.setToolTipText("If you are not able to connect to the official Android repository " +\r
+                "using HTTPS, enable this setting to force accessing it via HTTP.");\r
         mForceHttpCheck.addSelectionListener(new SelectionAdapter() {\r
             @Override\r
             public void widgetSelected(SelectionEvent e) {\r
@@ -100,6 +112,18 @@ public class SettingsPage extends Composite implements ISettingsPage {
             }\r
         });\r
 \r
+        mAskAdbRestartCheck = new Button(mMiscGroup, SWT.CHECK);\r
+        mAskAdbRestartCheck.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));\r
+        mAskAdbRestartCheck.setText("Ask before restarting ADB");\r
+        mAskAdbRestartCheck.setToolTipText("When checked, the user will be asked for permission " +\r
+                "to restart ADB after updating an addon-on package or a tool package.");\r
+        mAskAdbRestartCheck.addSelectionListener(new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent e) {\r
+                onForceHttpSelected();  //$hide$\r
+            }\r
+        });\r
+\r
         mApplyButton = new Button(this, SWT.NONE);\r
         mApplyButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
         mApplyButton.setText("Save && Apply");\r
@@ -139,6 +163,7 @@ public class SettingsPage extends Composite implements ISettingsPage {
         mProxyServerText.setText(in_settings.getProperty(KEY_HTTP_PROXY_HOST, ""));  //$NON-NLS-1$\r
         mProxyPortText.setText(  in_settings.getProperty(KEY_HTTP_PROXY_PORT, ""));  //$NON-NLS-1$\r
         mForceHttpCheck.setSelection(Boolean.parseBoolean(in_settings.getProperty(KEY_FORCE_HTTP)));\r
+        mAskAdbRestartCheck.setSelection(Boolean.parseBoolean(in_settings.getProperty(KEY_ASK_ADB_RESTART)));\r
 \r
         // We loaded fresh settings so there's nothing dirty to apply\r
         mApplyButton.setEnabled(false);\r
@@ -151,6 +176,8 @@ public class SettingsPage extends Composite implements ISettingsPage {
         out_settings.setProperty(KEY_HTTP_PROXY_PORT, mProxyPortText.getText());\r
         out_settings.setProperty(KEY_FORCE_HTTP,\r
                 Boolean.toString(mForceHttpCheck.getSelection()));\r
+        out_settings.setProperty(KEY_ASK_ADB_RESTART,\r
+                Boolean.toString(mAskAdbRestartCheck.getSelection()));\r
     }\r
 \r
     /**\r
index 293d66a..d72cd94 100644 (file)
@@ -218,15 +218,32 @@ final class AddOnTarget implements IAndroidTarget {
             return true;
         }
 
-        // if the receiver has optional libraries, then the target is only compatible if the
-        // vendor and name are the same
-        if (mLibraries.length != 0 &&
-                (mVendor.equals(target.getVendor()) == false ||
-                        mName.equals(target.getName()) == false)) {
-            return false;
+        /*
+         * The method javadoc indicates:
+         * Returns whether the given target is compatible with the receiver.
+         * <p/>A target is considered compatible if applications developed for the receiver can
+         * run on the given target.
+         */
+
+        // The receiver is an add-on. There are 2 big use cases: The add-on has libraries
+        // or the add-on doesn't (in which case we consider it a platform).
+        if (mLibraries.length == 0) {
+            return mBasePlatform.isCompatibleBaseFor(target);
+        } else {
+            // the only targets that can run the receiver are the same add-on in the same or later
+            // versions.
+            // first check: vendor/name
+            if (mVendor.equals(target.getVendor()) == false ||
+                            mName.equals(target.getName()) == false) {
+                return false;
+            }
+
+            // now check the version. At this point since we checked the add-on part,
+            // we can revert to the basic check on version/codename which are done by the
+            // base platform already.
+            return mBasePlatform.isCompatibleBaseFor(target);
         }
 
-        return mBasePlatform.equals(target);
     }
 
     public String hashString() {
index 61b348a..ef62f6e 100644 (file)
@@ -174,7 +174,7 @@ public class AndroidVersion {
         } else if (obj instanceof String) {
             // if we have a code name, this must match.
             if (mCodename != null) {
-                return mCodename.equals((String)obj);
+                return mCodename.equals(obj);
             }
 
             // else we try to convert to a int and compare to the api level
index 15e7e51..d0f009f 100644 (file)
@@ -242,7 +242,7 @@ final class PlatformTarget implements IAndroidTarget {
         int apiDiff = mVersion.getApiLevel() - target.getVersion().getApiLevel();
 
         if (mVersion.getCodename() != null && apiDiff == 0) {
-            if (target.getVersionName() == null) {
+            if (target.getVersion().getCodename() == null) {
                 return +1; // preview showed last
             }
             return mVersion.getCodename().compareTo(target.getVersion().getCodename());
index b260c89..4ef3468 100644 (file)
@@ -107,6 +107,10 @@ public final class SdkConstants {
     public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
             "emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
 
+    /** zipalign executable (with extension for the current OS)  */
+    public final static String FN_ZIPALIGN = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
+            "zipalign.exe" : "zipalign"; //$NON-NLS-1$ //$NON-NLS-2$
+
     /* Folder Names for Android Projects . */
 
     /** Resources folder name, i.e. "res". */
index 23f87e5..954da17 100644 (file)
@@ -911,7 +911,7 @@ public final class AvdManager {
                 replaceAvd(avdInfo, info);
 
                 // update the ini file
-                createAvdIniFile(avdInfo);
+                createAvdIniFile(info);
             }
 
             if (newName != null) {
index 1e16a83..916fa7c 100644 (file)
@@ -199,9 +199,10 @@ public class ProjectCreator {
             defaultProperties.setAndroidTarget(target);
             defaultProperties.save();
 
-            // create an empty build.properties
+            // create a build.properties file with just the application package
             ProjectProperties buildProperties = ProjectProperties.create(folderPath,
                     PropertyType.BUILD);
+            buildProperties.setProperty(ProjectProperties.PROPERTY_APP_PACKAGE, packageName);
             buildProperties.save();
 
             // create the map for place-holders of values to replace in the templates
index 33f8837..f2b73c0 100644 (file)
@@ -22,7 +22,6 @@ import com.android.sdklib.SdkManager;
 
 import java.io.File;
 import java.io.FileOutputStream;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.util.HashMap;
@@ -38,6 +37,7 @@ public final class ProjectProperties {
     public final static String PROPERTY_TARGET = "target";
     public final static String PROPERTY_APK_CONFIGS = "apk-configurations";
     public final static String PROPERTY_SDK = "sdk-location";
+    public final static String PROPERTY_APP_PACKAGE = "application-package";
 
     public static enum PropertyType {
         BUILD("build.properties", BUILD_HEADER),
@@ -85,15 +85,17 @@ public final class ProjectProperties {
            "# This file must be checked in Version Control Systems, as it is\n" +
            "# integral to the build system of your project.\n" +
            "\n" +
-           "# The name of your application package as defined in the manifest.\n" +
-           "# Used by the 'uninstall' rule.\n"+
-           "#application-package=com.example.myproject\n" +
+           "# This file is only used by the Ant script.\n" +
            "\n" +
-           "# The name of the source folder.\n" +
-           "#source-folder=src\n" +
+           "# You can use this to override default values such as\n" +
+           "#  'source-folder' for the location of your java source folder and\n" +
+           "#  'out-folder' for the location of your output folder.\n" +
            "\n" +
-           "# The name of the output folder.\n" +
-           "#out-folder=bin\n" +
+           "# You can also use it define how the release builds are signed by declaring\n" +
+           "# the following properties:\n" +
+           "#  'key.store' for the location of your keystore and\n" +
+           "#  'key.alias' for the name of the key to use.\n" +
+           "# The password will be asked during the build when you use the 'release' target.\n" +
            "\n";
 
     private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>();
@@ -116,6 +118,9 @@ public final class ProjectProperties {
                 "# location of the SDK. This is only used by Ant\n" +
                 "# For customization when using a Version Control System, please read the\n" +
                 "# header note.\n");
+        COMMENT_MAP.put(PROPERTY_APP_PACKAGE,
+                "# The name of your application package as defined in the manifest.\n" +
+                "# Used by the 'uninstall' rule.\n");
     }
 
     private final String mProjectFolderOsPath;
index 4a19206..b28019f 100755 (executable)
@@ -126,8 +126,12 @@ public class AddonPackage extends Package {
         super.saveProperties(props);\r
 \r
         mVersion.saveProperties(props);\r
-        props.setProperty(PROP_NAME, mName);\r
-        props.setProperty(PROP_VENDOR, mVendor);\r
+        if (mName != null) {\r
+            props.setProperty(PROP_NAME, mName);\r
+        }\r
+        if (mVendor != null) {\r
+            props.setProperty(PROP_VENDOR, mVendor);\r
+        }\r
     }\r
 \r
     /**\r
@@ -192,9 +196,9 @@ public class AddonPackage extends Package {
     /** Returns a long description for an {@link IDescription}. */\r
     @Override\r
     public String getLongDescription() {\r
-        return String.format("%1$s.\n%2$s",\r
+        return String.format("%1$s,\nRevision %2$d.",\r
                 getShortDescription(),\r
-                super.getLongDescription());\r
+                getRevision());\r
     }\r
 \r
     /**\r
index 3757b74..d7c5625 100755 (executable)
@@ -23,6 +23,7 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
 import org.apache.commons.compress.archivers.zip.ZipFile;\r
 \r
 import java.io.File;\r
+import java.io.FileNotFoundException;\r
 import java.io.FileOutputStream;\r
 import java.io.IOException;\r
 import java.io.InputStream;\r
@@ -572,6 +573,10 @@ public class Archive implements IDescription {
 \r
             return true;\r
 \r
+        } catch (FileNotFoundException e) {\r
+            // The FNF message is just the URL. Make it a bit more useful.\r
+            monitor.setResult("File not found: %1$s", e.getMessage());\r
+\r
         } catch (Exception e) {\r
             monitor.setResult(e.getMessage());\r
 \r
@@ -655,6 +660,7 @@ public class Archive implements IDescription {
             }\r
 \r
             // Swap the old folder by the new one.\r
+            File renameFailedForDir = null;\r
             if (destFolder.isDirectory()) {\r
                 renamedDestFolder = findTempFolder(osSdkRoot, pkgKind, "old");  //$NON-NLS-1$\r
                 if (renamedDestFolder == null) {\r
@@ -666,14 +672,26 @@ public class Archive implements IDescription {
                 if (!destFolder.renameTo(renamedDestFolder)) {\r
                     monitor.setResult("Failed to rename directory %1$s to %2$s",\r
                             destFolder.getPath(), renamedDestFolder.getPath());\r
-                    return false;\r
-\r
+                    renameFailedForDir = destFolder;\r
                 }\r
             }\r
 \r
-            if (!unzipDestFolder.renameTo(destFolder)) {\r
+            if (renameFailedForDir == null && !unzipDestFolder.renameTo(destFolder)) {\r
                 monitor.setResult("Failed to rename directory %1$s to %2$s",\r
                         unzipDestFolder.getPath(), destFolder.getPath());\r
+                renameFailedForDir = unzipDestFolder;\r
+            }\r
+\r
+            if (renameFailedForDir != null) {\r
+                if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) {\r
+                    monitor.setResult(\r
+                            "-= Warning ! =-\n" +\r
+                            "A folder failed to be renamed or moved. On Windows this " +\r
+                            "typically means that a program is using that folder (for example " +\r
+                            "Windows Explorer.) Please close all running programs that may be " +\r
+                            "locking the directory '%1$s' and try again.",\r
+                            renameFailedForDir.getPath());\r
+                }\r
                 return false;\r
             }\r
 \r
@@ -723,8 +741,7 @@ public class Archive implements IDescription {
 \r
             byte[] buf = new byte[65536];\r
 \r
-            Enumeration<ZipArchiveEntry> entries =\r
-                    (Enumeration<ZipArchiveEntry>)zipFile.getEntries();\r
+            Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();\r
             while (entries.hasMoreElements()) {\r
                 ZipArchiveEntry entry = entries.nextElement();\r
 \r
index abd42fb..75879b8 100755 (executable)
@@ -113,9 +113,9 @@ public class DocPackage extends Package {
     /** Returns a long description for an {@link IDescription}. */\r
     @Override\r
     public String getLongDescription() {\r
-        return String.format("%1$s.\n%2$s",\r
+        return String.format("%1$s,\nRevision %2$d.",\r
                 getShortDescription(),\r
-                super.getLongDescription());\r
+                getRevision());\r
     }\r
 \r
     /**\r
@@ -166,22 +166,25 @@ public class DocPackage extends Package {
 \r
         AndroidVersion replacementVersion = replacementDoc.getVersion();\r
 \r
-        // the new doc is an update if the api level is higher\r
+        // the new doc is an update if the api level is higher (no matter the codename on either)\r
         if (replacementVersion.getApiLevel() > mVersion.getApiLevel()) {\r
             return UpdateInfo.UPDATE;\r
         }\r
 \r
-        // if it's the exactly same (including codename), we check the revision\r
-        if (replacementVersion.equals(mVersion) &&\r
-                replacementPackage.getRevision() > this.getRevision()) {\r
-            return UpdateInfo.UPDATE;\r
-        }\r
-\r
-        // else we check if they have the same api level and the new one is a preview, in which\r
-        // case it's also an update (since preview have the api level of the _previous_ version.\r
-        if (replacementVersion.getApiLevel() == mVersion.getApiLevel() &&\r
-                replacementVersion.isPreview()) {\r
-            return UpdateInfo.UPDATE;\r
+        // Check if they're the same exact (api and codename)\r
+        if (replacementVersion.equals(mVersion)) {\r
+            // exact same version, so check the revision level\r
+            if (replacementPackage.getRevision() > this.getRevision()) {\r
+                return UpdateInfo.UPDATE;\r
+            }\r
+        } else {\r
+            // not the same version? we check if they have the same api level and the new one\r
+            // is a preview, in which case it's also an update (since preview have the api level\r
+            // of the _previous_ version.)\r
+            if (replacementVersion.getApiLevel() == mVersion.getApiLevel() &&\r
+                    replacementVersion.isPreview()) {\r
+                return UpdateInfo.UPDATE;\r
+            }\r
         }\r
 \r
         // not an upgrade but not incompatible either.\r
index 86d650b..74e1c59 100755 (executable)
@@ -33,11 +33,30 @@ import java.util.Properties;
  */\r
 public class ExtraPackage extends Package {\r
 \r
-    private static final String PROP_PATH = "Extra.Path";  //$NON-NLS-1$\r
+    private static final String PROP_PATH          = "Extra.Path";         //$NON-NLS-1$\r
+    private static final String PROP_MIN_TOOLS_REV = "Extra.MinToolsRev";  //$NON-NLS-1$\r
 \r
+    /**\r
+     * The install folder name. It must be a single-segment path.\r
+     * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used.\r
+     * This limitation cannot be written in the XML Schema and must be enforced here by using\r
+     * the method {@link #isPathValid()} *before* installing the package.\r
+     */\r
     private final String mPath;\r
 \r
     /**\r
+     * The minimal revision of the tools package required by this extra package, if > 0,\r
+     * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.\r
+     */\r
+    private final int mMinToolsRevision;\r
+\r
+    /**\r
+     * The value of {@link #mMinToolsRevision} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}\r
+     * was not specified in the XML source.\r
+     */\r
+    public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;\r
+\r
+    /**\r
      * Creates a new tool package from the attributes and elements of the given XML node.\r
      * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
@@ -45,6 +64,8 @@ public class ExtraPackage extends Package {
     ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
         super(source, packageNode, licenses);\r
         mPath = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_PATH);\r
+        mMinToolsRevision = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_TOOLS_REV,\r
+                MIN_TOOLS_REV_NOT_SPECIFIED);\r
     }\r
 \r
     /**\r
@@ -73,6 +94,9 @@ public class ExtraPackage extends Package {
                 archiveOsPath);\r
         // The path argument comes before whatever could be in the properties\r
         mPath = path != null ? path : getProperty(props, PROP_PATH, path);\r
+\r
+        mMinToolsRevision = Integer.parseInt(getProperty(props, PROP_MIN_TOOLS_REV,\r
+                Integer.toString(MIN_TOOLS_REV_NOT_SPECIFIED)));\r
     }\r
 \r
     /**\r
@@ -84,6 +108,10 @@ public class ExtraPackage extends Package {
         super.saveProperties(props);\r
 \r
         props.setProperty(PROP_PATH, mPath);\r
+\r
+        if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+            props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(mMinToolsRevision));\r
+        }\r
     }\r
 \r
     /**\r
@@ -109,19 +137,64 @@ public class ExtraPackage extends Package {
         return mPath;\r
     }\r
 \r
+    /**\r
+     * The minimal revision of the tools package required by this extra package, if > 0,\r
+     * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.\r
+     */\r
+    public int getMinToolsRevision() {\r
+        return mMinToolsRevision;\r
+    }\r
+\r
     /** Returns a short description for an {@link IDescription}. */\r
     @Override\r
     public String getShortDescription() {\r
-        return String.format("Extra %1$s package, revision %2$d", getPath(), getRevision());\r
+        String name = getPath();\r
+        if (name != null) {\r
+            // Uniformize all spaces in the name and upper case words.\r
+\r
+            name = name.replaceAll("[ _\t\f-]+", " ");     //$NON-NLS-1$ //$NON-NLS-2$\r
+\r
+            // Look at all lower case characters in range [1..n-1] and replace them by an upper\r
+            // case if they are preceded by a space. Also upper cases the first character of the\r
+            // string.\r
+            boolean changed = false;\r
+            char[] chars = name.toCharArray();\r
+            for (int n = chars.length - 1, i = 0; i < n; i++) {\r
+                if (Character.isLowerCase(chars[i]) && (i == 0 || chars[i - 1] == ' ')) {\r
+                    chars[i] = Character.toUpperCase(chars[i]);\r
+                    changed = true;\r
+                }\r
+            }\r
+            if (changed) {\r
+                name = new String(chars);\r
+            }\r
+        }\r
+\r
+        String s = String.format("%1$s package, revision %2$d",\r
+                name,\r
+                getRevision());\r
+\r
+        if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+            s += String.format(" (tools rev: %1$d)", mMinToolsRevision);\r
+        }\r
+\r
+        return s;\r
     }\r
 \r
     /** Returns a long description for an {@link IDescription}. */\r
     @Override\r
     public String getLongDescription() {\r
-        return String.format("Extra %1$s package, revision %2$d.\n%3$s",\r
+        String s = String.format("Extra %1$s package, revision %2$d",\r
                 getPath(),\r
-                getRevision(),\r
-                super.getLongDescription());\r
+                getRevision());\r
+\r
+        if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+            s += String.format(" (min tools rev.: %1$d)", mMinToolsRevision);\r
+        }\r
+\r
+        s += ".";\r
+\r
+        return s;\r
     }\r
 \r
     /**\r
index ca78b0b..23ec892 100755 (executable)
@@ -44,7 +44,11 @@ public class LocalSdkParser {
     }\r
 \r
     /**\r
-     * Returns the packages found by the last call to {@link #parseSdk(String, SdkManager, ISdkLog)}.\r
+     * Returns the packages found by the last call to\r
+     * {@link #parseSdk(String, SdkManager, ISdkLog)}.\r
+     * <p/>\r
+     * This returns initially returns null.\r
+     * Once the parseSdk() method has been called, this returns a possibly empty but non-null array.\r
      */\r
     public Package[] getPackages() {\r
         return mPackages;\r
index 1fcd6b1..69d526b 100755 (executable)
@@ -42,16 +42,20 @@ import java.util.Properties;
  */\r
 public abstract class Package implements IDescription {\r
 \r
-    private static final String PROP_REVISION    = "Pkg.Revision";     //$NON-NLS-1$\r
-    private static final String PROP_LICENSE     = "Pkg.License";      //$NON-NLS-1$\r
-    private static final String PROP_DESC        = "Pkg.Desc";         //$NON-NLS-1$\r
-    private static final String PROP_DESC_URL    = "Pkg.DescUrl";      //$NON-NLS-1$\r
-    private static final String PROP_SOURCE_URL  = "Pkg.SourceUrl";    //$NON-NLS-1$\r
-    private static final String PROP_USER_SOURCE = "Pkg.UserSrc";      //$NON-NLS-1$\r
+    private static final String PROP_REVISION     = "Pkg.Revision";     //$NON-NLS-1$\r
+    private static final String PROP_LICENSE      = "Pkg.License";      //$NON-NLS-1$\r
+    private static final String PROP_DESC         = "Pkg.Desc";         //$NON-NLS-1$\r
+    private static final String PROP_DESC_URL     = "Pkg.DescUrl";      //$NON-NLS-1$\r
+    private static final String PROP_RELEASE_NOTE = "Pkg.RelNote";      //$NON-NLS-1$\r
+    private static final String PROP_RELEASE_URL  = "Pkg.RelNoteUrl";   //$NON-NLS-1$\r
+    private static final String PROP_SOURCE_URL   = "Pkg.SourceUrl";    //$NON-NLS-1$\r
+    private static final String PROP_USER_SOURCE  = "Pkg.UserSrc";      //$NON-NLS-1$\r
     private final int mRevision;\r
     private final String mLicense;\r
     private final String mDescription;\r
     private final String mDescUrl;\r
+    private final String mReleaseNote;\r
+    private final String mReleaseUrl;\r
     private final Archive[] mArchives;\r
     private final RepoSource mSource;\r
 \r
@@ -80,6 +84,8 @@ public abstract class Package implements IDescription {
         mRevision    = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_REVISION, 0);\r
         mDescription = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_DESCRIPTION);\r
         mDescUrl     = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_DESC_URL);\r
+        mReleaseNote = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_RELEASE_NOTE);\r
+        mReleaseUrl  = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_RELEASE_URL);\r
 \r
         mLicense  = parseLicense(packageNode, licenses);\r
         mArchives = parseArchives(XmlParserUtils.getFirstChild(\r
@@ -104,10 +110,19 @@ public abstract class Package implements IDescription {
             Arch archiveArch,\r
             String archiveOsPath) {\r
 \r
+        if (description == null) {\r
+            description = "";\r
+        }\r
+        if (descUrl == null) {\r
+            descUrl = "";\r
+        }\r
+\r
         mRevision = Integer.parseInt(getProperty(props, PROP_REVISION, Integer.toString(revision)));\r
-        mLicense     = getProperty(props, PROP_LICENSE,  license);\r
-        mDescription = getProperty(props, PROP_DESC,     description);\r
-        mDescUrl     = getProperty(props, PROP_DESC_URL, descUrl);\r
+        mLicense     = getProperty(props, PROP_LICENSE,      license);\r
+        mDescription = getProperty(props, PROP_DESC,         description);\r
+        mDescUrl     = getProperty(props, PROP_DESC_URL,     descUrl);\r
+        mReleaseNote = getProperty(props, PROP_RELEASE_NOTE, "");\r
+        mReleaseUrl  = getProperty(props, PROP_RELEASE_URL,  "");\r
 \r
         // If source is null and we can find a source URL in the properties, generate\r
         // a dummy source just to store the URL. This allows us to easily remember where\r
@@ -145,9 +160,23 @@ public abstract class Package implements IDescription {
      */\r
     void saveProperties(Properties props) {\r
         props.setProperty(PROP_REVISION, Integer.toString(mRevision));\r
-        props.setProperty(PROP_LICENSE, mLicense);\r
-        props.setProperty(PROP_DESC, mDescription);\r
-        props.setProperty(PROP_DESC_URL, mDescUrl);\r
+        if (mLicense != null && mLicense.length() > 0) {\r
+            props.setProperty(PROP_LICENSE, mLicense);\r
+        }\r
+\r
+        if (mDescription != null && mDescription.length() > 0) {\r
+            props.setProperty(PROP_DESC, mDescription);\r
+        }\r
+        if (mDescUrl != null && mDescUrl.length() > 0) {\r
+            props.setProperty(PROP_DESC_URL, mDescUrl);\r
+        }\r
+\r
+        if (mReleaseNote != null && mReleaseNote.length() > 0) {\r
+            props.setProperty(PROP_RELEASE_NOTE, mReleaseNote);\r
+        }\r
+        if (mReleaseUrl != null && mReleaseUrl.length() > 0) {\r
+            props.setProperty(PROP_RELEASE_URL, mReleaseUrl);\r
+        }\r
 \r
         if (mSource != null) {\r
             props.setProperty(PROP_SOURCE_URL,  mSource.getUrl());\r
@@ -253,6 +282,22 @@ public abstract class Package implements IDescription {
     }\r
 \r
     /**\r
+     * Returns the optional release note for all packages (platform, add-on, tool, doc) or\r
+     * for a lib. Can be empty but not null.\r
+     */\r
+    public String getReleaseNote() {\r
+        return mReleaseNote;\r
+    }\r
+\r
+    /**\r
+     * Returns the optional release note URL for all packages (platform, add-on, tool, doc).\r
+     * Can be empty but not null.\r
+     */\r
+    public String getReleaseNoteUrl() {\r
+        return mReleaseUrl;\r
+    }\r
+\r
+    /**\r
      * Returns the archives defined in this package.\r
      * Can be an empty array but not null.\r
      */\r
@@ -285,7 +330,34 @@ public abstract class Package implements IDescription {
      * Can be empty but not null.\r
      */\r
     public String getLongDescription() {\r
-        return String.format("%1$s\nRevision %2$d", getDescription(), getRevision());\r
+        StringBuilder sb = new StringBuilder();\r
+\r
+        String s = getDescription();\r
+        if (s != null) {\r
+            sb.append(s);\r
+        }\r
+        if (sb.length() > 0) {\r
+            sb.append("\n");\r
+        }\r
+\r
+        sb.append(String.format("Revision %1$d", getRevision()));\r
+\r
+        s = getDescUrl();\r
+        if (s != null && s.length() > 0) {\r
+            sb.append(String.format("\n\nMore information at %1$s", s));\r
+        }\r
+\r
+        s = getReleaseNote();\r
+        if (s != null && s.length() > 0) {\r
+            sb.append("\n\nRelease note:\n").append(s);\r
+        }\r
+\r
+        s = getReleaseNoteUrl();\r
+        if (s != null && s.length() > 0) {\r
+            sb.append("\nRelease note URL: ").append(s);\r
+        }\r
+\r
+        return sb.toString();\r
     }\r
 \r
     /**\r
@@ -326,7 +398,7 @@ public abstract class Package implements IDescription {
      * current one, it's not an update.\r
      *\r
      * @param replacementPackage The potential replacement package.\r
-     * @return\r
+     * @return One of the {@link UpdateInfo} values.\r
      *\r
      * @see #sameItemAs(Package)\r
      */\r
index 324f51d..e95656a 100755 (executable)
@@ -35,12 +35,28 @@ import java.util.Properties;
  */\r
 public class PlatformPackage extends Package {\r
 \r
-    private static final String PROP_VERSION   = "Platform.Version";   //$NON-NLS-1$\r
+    private static final String PROP_VERSION       = "Platform.Version";      //$NON-NLS-1$\r
+    private static final String PROP_MIN_TOOLS_REV = "Platform.MinToolsRev";  //$NON-NLS-1$\r
 \r
+    /** The package version, for platform, add-on and doc packages. */\r
     private final AndroidVersion mVersion;\r
+\r
+    /** The version, a string, for platform packages. */\r
     private final String mVersionName;\r
 \r
     /**\r
+     * The minimal revision of the tools package required by this extra package, if > 0,\r
+     * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.\r
+     */\r
+    private final int mMinToolsRevision;\r
+\r
+    /**\r
+     * The value of {@link #mMinToolsRevision} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}\r
+     * was not specified in the XML source.\r
+     */\r
+    public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;\r
+\r
+    /**\r
      * Creates a new platform package from the attributes and elements of the given XML node.\r
      * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
@@ -54,6 +70,9 @@ public class PlatformPackage extends Package {
             codeName = null;\r
         }\r
         mVersion = new AndroidVersion(apiLevel, codeName);\r
+\r
+        mMinToolsRevision = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_TOOLS_REV,\r
+                MIN_TOOLS_REV_NOT_SPECIFIED);\r
     }\r
 \r
     /**\r
@@ -76,6 +95,9 @@ public class PlatformPackage extends Package {
 \r
         mVersion = target.getVersion();\r
         mVersionName  = target.getVersionName();\r
+\r
+        mMinToolsRevision = Integer.parseInt(getProperty(props, PROP_MIN_TOOLS_REV,\r
+                Integer.toString(MIN_TOOLS_REV_NOT_SPECIFIED)));\r
     }\r
 \r
     /**\r
@@ -87,7 +109,14 @@ public class PlatformPackage extends Package {
         super.saveProperties(props);\r
 \r
         mVersion.saveProperties(props);\r
-        props.setProperty(PROP_VERSION, mVersionName);\r
+\r
+        if (mVersionName != null) {\r
+            props.setProperty(PROP_VERSION, mVersionName);\r
+        }\r
+\r
+        if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+            props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(mMinToolsRevision));\r
+        }\r
     }\r
 \r
     /** Returns the version, a string, for platform packages. */\r
@@ -100,25 +129,38 @@ public class PlatformPackage extends Package {
         return mVersion;\r
     }\r
 \r
+    /**\r
+     * The minimal revision of the tools package required by this extra package, if > 0,\r
+     * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.\r
+     */\r
+    public int getMinToolsRevision() {\r
+        return mMinToolsRevision;\r
+    }\r
+\r
     /** Returns a short description for an {@link IDescription}. */\r
     @Override\r
     public String getShortDescription() {\r
-        if (mVersion.isPreview()) {\r
-            return String.format("SDK Platform Android %1$s (Preview)",\r
-                    getVersionName());\r
-        }\r
+        String s;\r
 \r
-        return String.format("SDK Platform Android %1$s, API %2$d",\r
+        if (mVersion.isPreview()) {\r
+            s = String.format("SDK Platform Android %1$s (Preview)", getVersionName());\r
+        } else {\r
+            s = String.format("SDK Platform Android %1$s, API %2$d",\r
                 getVersionName(),\r
                 mVersion.getApiLevel());\r
+        }\r
+\r
+        if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+            s += String.format(" (tools rev: %1$d)", mMinToolsRevision);\r
+        }\r
+\r
+        return s;\r
     }\r
 \r
     /** Returns a long description for an {@link IDescription}. */\r
     @Override\r
     public String getLongDescription() {\r
-        return String.format("%1$s.\n%2$s",\r
-                getShortDescription(),\r
-                super.getLongDescription());\r
+        return getShortDescription() + ".";\r
     }\r
 \r
     /**\r
index 5de2e94..d86859d 100755 (executable)
@@ -31,6 +31,7 @@ import java.net.URL;
 import java.util.ArrayList;\r
 import java.util.HashMap;\r
 \r
+import javax.net.ssl.SSLKeyException;\r
 import javax.xml.XMLConstants;\r
 import javax.xml.parsers.DocumentBuilder;\r
 import javax.xml.parsers.DocumentBuilderFactory;\r
@@ -47,7 +48,7 @@ import javax.xml.validation.Validator;
  */\r
 public class RepoSource implements IDescription {\r
 \r
-    private final String mUrl;\r
+    private String mUrl;\r
     private final boolean mUserSource;\r
 \r
     private Package[] mPackages;\r
@@ -56,6 +57,9 @@ public class RepoSource implements IDescription {
 \r
     /**\r
      * Constructs a new source for the given repository URL.\r
+     * @param url The source URL. Cannot be null. If the URL ends with a /, the default\r
+     *            repository.xml filename will be appended automatically.\r
+     * @param userSource True if this a user source (add-ons & packages only.)\r
      */\r
     public RepoSource(String url, boolean userSource) {\r
 \r
@@ -72,6 +76,23 @@ public class RepoSource implements IDescription {
         setDefaultDescription();\r
     }\r
 \r
+    /**\r
+     * Two repo source are equal if they have the same userSource flag and the same URL.\r
+     */\r
+    @Override\r
+    public boolean equals(Object obj) {\r
+        if (obj instanceof RepoSource) {\r
+            RepoSource rs = (RepoSource) obj;\r
+            return  rs.isUserSource() == this.isUserSource() && rs.getUrl().equals(this.getUrl());\r
+        }\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    public int hashCode() {\r
+        return mUrl.hashCode() ^ Boolean.valueOf(mUserSource).hashCode();\r
+    }\r
+\r
     /** Returns true if this is a user source. We only load addon and extra packages\r
      * from a user source and ignore the rest. */\r
     public boolean isUserSource() {\r
@@ -132,55 +153,53 @@ public class RepoSource implements IDescription {
         monitor.setDescription("Fetching %1$s", url);\r
         monitor.incProgress(1);\r
 \r
-        ByteArrayInputStream xml = null;\r
-\r
-        for (int tentative = 0; tentative < 2 && xml == null; tentative++) {\r
-\r
-            // reset fetch error and fetch\r
-            mFetchError = null;\r
-            xml = fetchUrl(url, monitor);\r
+        mFetchError = null;\r
+        Exception[] exception = new Exception[] { null };\r
+        ByteArrayInputStream xml = fetchUrl(url, exception);\r
+        boolean validated = false;\r
+        if (xml != null) {\r
+            monitor.setDescription("Validate XML");\r
+            validated = validateXml(xml, url, monitor);\r
+        }\r
 \r
-            if (xml == null) {\r
-                mDescription += String.format("\nFailed to fetch URL %1$s", url);\r
-                mFetchError = "Failed to fetch URL";\r
-                monitor.setResult("Failed to fetch URL %1$s", url);\r
-            } else {\r
-                // We got a document. It might not be XML or it might not be valid.\r
+        // If we failed the first time and the URL doesn't explicitly end with\r
+        // our filename, make another tentative after changing the URL.\r
+        if (!validated && !url.endsWith(SdkRepository.URL_DEFAULT_XML_FILE)) {\r
+            if (!url.endsWith("/")) {       //$NON-NLS-1$\r
+                url += "/";                 //$NON-NLS-1$\r
+            }\r
+            url += SdkRepository.URL_DEFAULT_XML_FILE;\r
 \r
-                monitor.setDescription("Validate XML");\r
+            xml = fetchUrl(url, exception);\r
+            if (xml != null) {\r
+                validated = validateXml(xml, url, monitor);\r
+            }\r
 \r
-                if (validateXml(xml, url, monitor)) {\r
-                    // We got a valid XML, keep it and use it.\r
+            if (validated) {\r
+                // If the second tentative succeeded, indicate it in the console\r
+                // with the URL that worked.\r
+                monitor.setResult("Repository found at %1$s", url);\r
 \r
-                    if (tentative > 0) {\r
-                        // If the second tentative succeeded, indicate it in the console,\r
-                        // otherwise the user will only see the first failure\r
-                        // message and will think the whole thing failed. This also\r
-                        // indicates we modifed the URL.\r
-                        monitor.setResult("Repository found instead at %1$s", url);\r
-                    }\r
-                    break;\r
-                } else {\r
-                    mDescription += String.format("\nFailed to validate XML at %1$s", url);\r
-                    mFetchError = "Failed to validate XML";\r
-                    monitor.setResult("Failed to validate XML at %1$s", url);\r
-\r
-                    // forget this XML, it wasn't any good.\r
-                    xml = null;\r
-                }\r
+                // Keep the modified URL\r
+                mUrl = url;\r
             }\r
+        }\r
 \r
-            // If we failed the first time and the URL doesn't explicitly end with\r
-            // our filename, make another tentative. Otherwise abort.\r
-            if (tentative == 0 && !url.endsWith(SdkRepository.URL_DEFAULT_XML_FILE)) {\r
-                if (!url.endsWith("/")) {       //$NON-NLS-1$\r
-                    url += "/";                 //$NON-NLS-1$\r
+        if (!validated) {\r
+            mFetchError = "Failed to fetch URL";\r
+\r
+            String reason = "Unknown";\r
+            if (exception[0] != null) {\r
+                if (exception[0] instanceof FileNotFoundException) {\r
+                    reason = "File not found";\r
+                } else if (exception[0] instanceof SSLKeyException) {\r
+                    reason = "SSL error. You might want to force download through http in the settings.";\r
+                } else if (exception[0].getMessage() != null) {\r
+                    reason = exception[0].getMessage();\r
                 }\r
-                url += SdkRepository.URL_DEFAULT_XML_FILE;\r
-            } else {\r
-                break;\r
             }\r
 \r
+            monitor.setResult("Failed to fetch URL %1$s, reason: %2$s", url, reason);\r
         }\r
 \r
         monitor.incProgress(1);\r
@@ -189,7 +208,7 @@ public class RepoSource implements IDescription {
             monitor.setDescription("Parse XML");\r
             monitor.incProgress(1);\r
             parsePackages(xml, monitor);\r
-            if (mPackages.length == 0) {\r
+            if (mPackages == null || mPackages.length == 0) {\r
                 mDescription += "\nNo packages found.";\r
             } else if (mPackages.length == 1) {\r
                 mDescription += "\nOne package found.";\r
@@ -219,7 +238,7 @@ public class RepoSource implements IDescription {
      * Java URL Reader: http://java.sun.com/docs/books/tutorial/networking/urls/readingURL.html\r
      * Java set Proxy: http://java.sun.com/docs/books/tutorial/networking/urls/_setProxy.html\r
      */\r
-    private ByteArrayInputStream fetchUrl(String urlString, ITaskMonitor monitor) {\r
+    private ByteArrayInputStream fetchUrl(String urlString, Exception[] outException) {\r
         URL url;\r
         try {\r
             url = new URL(urlString);\r
@@ -255,12 +274,8 @@ public class RepoSource implements IDescription {
                 }\r
             }\r
 \r
-        } catch (FileNotFoundException e) {\r
-            // The FNF message is just the URL. Make it a bit more useful.\r
-            monitor.setResult("File not found: %1$s", e.getMessage());\r
-\r
         } catch (IOException e) {\r
-            monitor.setResult(e.getMessage());\r
+            outException[0] = e;\r
         }\r
 \r
         return null;\r
@@ -378,10 +393,10 @@ public class RepoSource implements IDescription {
             }\r
 \r
         } catch (ParserConfigurationException e) {\r
-            monitor.setResult("Failed to create XML document builder for %1$s");\r
+            monitor.setResult("Failed to create XML document builder");\r
 \r
         } catch (SAXException e) {\r
-            monitor.setResult("Failed to parse XML document %1$s");\r
+            monitor.setResult("Failed to parse XML document");\r
 \r
         } catch (IOException e) {\r
             monitor.setResult("Failed to read XML document");\r
index 7af6657..e0452b8 100755 (executable)
@@ -95,7 +95,10 @@ public class RepoSources {
                 for (int i = 0; i < count; i++) {\r
                     String url = props.getProperty(String.format("%s%02d", KEY_SRC, i));  //$NON-NLS-1$\r
                     if (url != null) {\r
-                        mSources.add(new RepoSource(url, true /*userSource*/));\r
+                        RepoSource s = new RepoSource(url, true /*userSource*/);\r
+                        if (!hasSource(s)) {\r
+                            mSources.add(s);\r
+                        }\r
                     }\r
                 }\r
             }\r
@@ -120,6 +123,20 @@ public class RepoSources {
     }\r
 \r
     /**\r
+     * Returns true if there's already a similar source in the sources list.\r
+     * <p/>\r
+     * The search is O(N), which should be acceptable on the expectedly small source list.\r
+     */\r
+    public boolean hasSource(RepoSource source) {\r
+        for (RepoSource s : mSources) {\r
+            if (s.equals(source)) {\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
      * Saves all the user sources.\r
      * @param log\r
      */\r
index bb17874..ee13379 100755 (executable)
@@ -76,9 +76,7 @@ public class ToolPackage extends Package {
     /** Returns a long description for an {@link IDescription}. */\r
     @Override\r
     public String getLongDescription() {\r
-        return String.format("Android SDK Tools, revision %1$d.\n%2$s",\r
-                getRevision(),\r
-                super.getLongDescription());\r
+        return getShortDescription() + ".";\r
     }\r
 \r
     /**\r
index 88d18db..2bf87d7 100755 (executable)
@@ -49,15 +49,21 @@ public class SdkRepository {
     public static final String NODE_EXTRA    = "extra";                         //$NON-NLS-1$\r
 \r
     /** The license definition. */\r
-    public static final String NODE_LICENSE = "license";                        //$NON-NLS-1$\r
+    public static final String NODE_LICENSE       = "license";                  //$NON-NLS-1$\r
     /** The optional uses-license for all packages (platform, add-on, tool, doc) or for a lib. */\r
-    public static final String NODE_USES_LICENSE = "uses-license";              //$NON-NLS-1$\r
+    public static final String NODE_USES_LICENSE  = "uses-license";             //$NON-NLS-1$\r
     /** The revision, an int > 0, for all packages (platform, add-on, tool, doc). */\r
-    public static final String NODE_REVISION    = "revision";                   //$NON-NLS-1$\r
+    public static final String NODE_REVISION      = "revision";                 //$NON-NLS-1$\r
     /** The optional description for all packages (platform, add-on, tool, doc) or for a lib. */\r
-    public static final String NODE_DESCRIPTION = "description";                //$NON-NLS-1$\r
+    public static final String NODE_DESCRIPTION   = "description";              //$NON-NLS-1$\r
     /** The optional description URL for all packages (platform, add-on, tool, doc). */\r
-    public static final String NODE_DESC_URL    = "desc-url";                   //$NON-NLS-1$\r
+    public static final String NODE_DESC_URL      = "desc-url";                 //$NON-NLS-1$\r
+    /** The optional release note for all packages (platform, add-on, tool, doc). */\r
+    public static final String NODE_RELEASE_NOTE  = "release-note";             //$NON-NLS-1$\r
+    /** The optional release note URL for all packages (platform, add-on, tool, doc). */\r
+    public static final String NODE_RELEASE_URL   = "release-url";              //$NON-NLS-1$\r
+    /** The optional minimal tools revision required by platform & extra packages. */\r
+    public static final String NODE_MIN_TOOLS_REV = "min-tools-rev";            //$NON-NLS-1$\r
 \r
     /** The version, a string, for platform packages. */\r
     public static final String NODE_VERSION   = "version";                      //$NON-NLS-1$\r
index a697c83..f00e337 100755 (executable)
                             <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
                             <!-- The optional description URL of this package -->
                             <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+                            <!-- The optional release note for this package. -->
+                            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+                            <!-- The optional release note URL of this package -->
+                            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
                             <!-- A list of file archives for this package. -->
                             <xsd:element name="archives"     type="sdk:archivesType" />
+                            <!-- The minimal revision of tools required by this package.
+                                 Optional. If present, must be an int > 0. -->
+                            <xsd:element name="min-tools-rev" type="xsd:positiveInteger" minOccurs="0" />
                         </xsd:all>
                     </xsd:complexType>
                 </xsd:element>
                             <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
                             <!-- The optional description URL of this package -->
                             <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+                            <!-- The optional release note for this package. -->
+                            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+                            <!-- The optional release note URL of this package -->
+                            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
                             <!-- A list of file archives for this package. -->
                             <xsd:element name="archives"     type="sdk:archivesType" />
 
                             <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
                             <!-- The optional description URL of this package -->
                             <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+                            <!-- The optional release note for this package. -->
+                            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+                            <!-- The optional release note URL of this package -->
+                            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
                             <!-- A list of file archives for this package. -->
                             <xsd:element name="archives"     type="sdk:archivesType" />
                         </xsd:all>
                             <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
                             <!-- The optional description URL of this package -->
                             <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+                            <!-- The optional release note for this package. -->
+                            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+                            <!-- The optional release note URL of this package -->
+                            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
                             <!-- A list of file archives for this package. -->
                             <xsd:element name="archives"     type="sdk:archivesType" />
                         </xsd:all>
                             <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
                             <!-- The optional description URL of this package -->
                             <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+                            <!-- The optional release note for this package. -->
+                            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+                            <!-- The optional release note URL of this package -->
+                            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
                             <!-- A list of file archives for this package. -->
                             <xsd:element name="archives"     type="sdk:archivesType" />
+                            <!-- The minimal revision of tools required by this package.
+                                 Optional. If present, must be an int > 0. -->
+                            <xsd:element name="min-tools-rev" type="xsd:positiveInteger" minOccurs="0" />
                         </xsd:all>
                     </xsd:complexType>
                 </xsd:element>
index 7340549..c90e18d 100755 (executable)
         <sdk:uses-license ref="license1" />\r
         <sdk:description>Some optional description</sdk:description>\r
         <sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>\r
+        <sdk:release-note>This is an optional release note\r
+            for this package. It's a free multi-line text.\r
+        </sdk:release-note>\r
+        <sdk:release-url>http://some/url/for/the/release/note.html</sdk:release-url>\r
+        <sdk:min-tools-rev>2</sdk:min-tools-rev>\r
         <!-- The archives node is mandatory and it cannot be empty. -->\r
         <sdk:archives>\r
             <sdk:archive os="any">\r
         </sdk:archives>\r
         <sdk:description>An Extra package for the USB driver, it will install in $SDK/usb_driver</sdk:description>\r
         <sdk:desc-url>http://www.example.com/extra.html</sdk:desc-url>\r
+        <sdk:min-tools-rev>3</sdk:min-tools-rev>\r
     </sdk:extra>\r
 \r
 </sdk:sdk-repository>\r
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AdbWrapper.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AdbWrapper.java
new file mode 100755 (executable)
index 0000000..cb553d1
--- /dev/null
@@ -0,0 +1,221 @@
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package com.android.sdkuilib.internal.repository;\r
+\r
+import com.android.sdklib.SdkConstants;\r
+import com.android.sdklib.internal.repository.ITaskMonitor;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStreamReader;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * A lightweight wrapper to start & stop ADB.\r
+ */\r
+public class AdbWrapper {\r
+\r
+    /*\r
+     * Note: we could bring ddmlib in SdkManager for that purpose, however this allows us to\r
+     * specialize the start/stop methods to our needs (e.g. a task monitor, etc.)\r
+     */\r
+\r
+    private final String mAdbOsLocation;\r
+    private final ITaskMonitor mMonitor;\r
+\r
+    /**\r
+     * Creates a new lightweight ADB wrapper.\r
+     *\r
+     * @param osSdkPath The root OS path of the SDK. Cannot be null.\r
+     * @param monitor A logger object. Cannot be null.\r
+     */\r
+    public AdbWrapper(String osSdkPath, ITaskMonitor monitor) {\r
+        mMonitor = monitor;\r
+\r
+        if (!osSdkPath.endsWith(File.separator)) {\r
+            osSdkPath += File.separator;\r
+        }\r
+        mAdbOsLocation = osSdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ADB;\r
+    }\r
+\r
+    private void display(String format, Object...args) {\r
+        mMonitor.setResult(format, args);\r
+    }\r
+\r
+    /**\r
+     * Starts the adb host side server.\r
+     * @return true if success\r
+     */\r
+    public synchronized boolean startAdb() {\r
+        if (mAdbOsLocation == null) {\r
+            display("Error: missing path to ADB."); //$NON-NLS-1$\r
+            return false;\r
+        }\r
+\r
+        Process proc;\r
+        int status = -1;\r
+\r
+        try {\r
+            String[] command = new String[2];\r
+            command[0] = mAdbOsLocation;\r
+            command[1] = "start-server"; //$NON-NLS-1$\r
+            proc = Runtime.getRuntime().exec(command);\r
+\r
+            ArrayList<String> errorOutput = new ArrayList<String>();\r
+            ArrayList<String> stdOutput = new ArrayList<String>();\r
+            status = grabProcessOutput(proc, errorOutput, stdOutput,\r
+                    false /* waitForReaders */);\r
+\r
+        } catch (IOException ioe) {\r
+            display("Unable to run 'adb': %1$s.", ioe.getMessage()); //$NON-NLS-1$\r
+            // we'll return false;\r
+        } catch (InterruptedException ie) {\r
+            display("Unable to run 'adb': %1$s.", ie.getMessage()); //$NON-NLS-1$\r
+            // we'll return false;\r
+        }\r
+\r
+        if (status != 0) {\r
+            display("'adb start-server' failed."); //$NON-NLS-1$\r
+            return false;\r
+        }\r
+\r
+        display("'adb start-server' succeeded."); //$NON-NLS-1$\r
+\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Stops the adb host side server.\r
+     * @return true if success\r
+     */\r
+    public synchronized boolean stopAdb() {\r
+        if (mAdbOsLocation == null) {\r
+            display("Error: missing path to ADB."); //$NON-NLS-1$\r
+            return false;\r
+        }\r
+\r
+        Process proc;\r
+        int status = -1;\r
+\r
+        try {\r
+            String[] command = new String[2];\r
+            command[0] = mAdbOsLocation;\r
+            command[1] = "kill-server"; //$NON-NLS-1$\r
+            proc = Runtime.getRuntime().exec(command);\r
+            status = proc.waitFor();\r
+        }\r
+        catch (IOException ioe) {\r
+            // we'll return false;\r
+        }\r
+        catch (InterruptedException ie) {\r
+            // we'll return false;\r
+        }\r
+\r
+        if (status != 0) {\r
+            display("'adb kill-server' failed -- run manually if necessary."); //$NON-NLS-1$\r
+            return false;\r
+        }\r
+\r
+        display("'adb kill-server' succeeded."); //$NON-NLS-1$\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Get the stderr/stdout outputs of a process and return when the process is done.\r
+     * Both <b>must</b> be read or the process will block on windows.\r
+     * @param process The process to get the ouput from\r
+     * @param errorOutput The array to store the stderr output. cannot be null.\r
+     * @param stdOutput The array to store the stdout output. cannot be null.\r
+     * @param waitforReaders if true, this will wait for the reader threads.\r
+     * @return the process return code.\r
+     * @throws InterruptedException\r
+     */\r
+    private int grabProcessOutput(final Process process, final ArrayList<String> errorOutput,\r
+            final ArrayList<String> stdOutput, boolean waitforReaders)\r
+            throws InterruptedException {\r
+        assert errorOutput != null;\r
+        assert stdOutput != null;\r
+        // read the lines as they come. if null is returned, it's\r
+        // because the process finished\r
+        Thread t1 = new Thread("") { //$NON-NLS-1$\r
+            @Override\r
+            public void run() {\r
+                // create a buffer to read the stderr output\r
+                InputStreamReader is = new InputStreamReader(process.getErrorStream());\r
+                BufferedReader errReader = new BufferedReader(is);\r
+\r
+                try {\r
+                    while (true) {\r
+                        String line = errReader.readLine();\r
+                        if (line != null) {\r
+                            display("ADB Error: %1$s", line);\r
+                            errorOutput.add(line);\r
+                        } else {\r
+                            break;\r
+                        }\r
+                    }\r
+                } catch (IOException e) {\r
+                    // do nothing.\r
+                }\r
+            }\r
+        };\r
+\r
+        Thread t2 = new Thread("") { //$NON-NLS-1$\r
+            @Override\r
+            public void run() {\r
+                InputStreamReader is = new InputStreamReader(process.getInputStream());\r
+                BufferedReader outReader = new BufferedReader(is);\r
+\r
+                try {\r
+                    while (true) {\r
+                        String line = outReader.readLine();\r
+                        if (line != null) {\r
+                            display("ADB: %1$s", line);\r
+                            stdOutput.add(line);\r
+                        } else {\r
+                            break;\r
+                        }\r
+                    }\r
+                } catch (IOException e) {\r
+                    // do nothing.\r
+                }\r
+            }\r
+        };\r
+\r
+        t1.start();\r
+        t2.start();\r
+\r
+        // it looks like on windows process#waitFor() can return\r
+        // before the thread have filled the arrays, so we wait for both threads and the\r
+        // process itself.\r
+        if (waitforReaders) {\r
+            try {\r
+                t1.join();\r
+            } catch (InterruptedException e) {\r
+            }\r
+            try {\r
+                t2.join();\r
+            } catch (InterruptedException e) {\r
+            }\r
+        }\r
+\r
+        // get the return code from the process\r
+        return process.waitFor();\r
+    }\r
+\r
+}\r
index 78b04f3..0d7179c 100755 (executable)
@@ -24,14 +24,34 @@ import java.util.Properties;
  */\r
 public interface ISettingsPage {\r
 \r
-    /** Java system setting picked up by {@link URL} for http proxy port. Type: String. */\r
+    /**\r
+     * Java system setting picked up by {@link URL} for http proxy port.\r
+     * Type: String.\r
+     */\r
     public static final String KEY_HTTP_PROXY_PORT = "http.proxyPort";           //$NON-NLS-1$\r
-    /** Java system setting picked up by {@link URL} for http proxy host. Type: String. */\r
+    /**\r
+     * Java system setting picked up by {@link URL} for http proxy host.\r
+     * Type: String.\r
+     */\r
     public static final String KEY_HTTP_PROXY_HOST = "http.proxyHost";           //$NON-NLS-1$\r
-    /** Setting to force using http:// instead of https:// connections. Type: Boolean. */\r
+    /**\r
+     * Setting to force using http:// instead of https:// connections.\r
+     * Type: Boolean.\r
+     * Default: False.\r
+     */\r
     public static final String KEY_FORCE_HTTP = "sdkman.force.http";             //$NON-NLS-1$\r
-    /** Setting to display only packages that are new or updates. Type: Boolean. */\r
+    /**\r
+     * Setting to display only packages that are new or updates.\r
+     * Type: Boolean.\r
+     * Default: True.\r
+     */\r
     public static final String KEY_SHOW_UPDATE_ONLY = "sdkman.show.update.only"; //$NON-NLS-1$\r
+    /**\r
+     * Setting to ask for permission before restarting ADB.\r
+     * Type: Boolean.\r
+     * Default: True.\r
+     */\r
+    public static final String KEY_ASK_ADB_RESTART = "sdkman.ask.adb.restart";   //$NON-NLS-1$\r
 \r
     /** Loads settings from the given {@link Properties} container and update the page UI. */\r
     public abstract void loadSettings(Properties in_settings);\r
index a7c1a83..1898f66 100755 (executable)
@@ -62,7 +62,6 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
     private Group mDescriptionContainer;\r
     private Button mAddSiteButton;\r
     private Button mDeleteSiteButton;\r
-    private Label mPlaceholder3;\r
     private Button mRefreshButton;\r
     private Button mInstallSelectedButton;\r
     private Label mDescriptionLabel;\r
@@ -106,28 +105,6 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
         mColumnSource.setWidth(289);\r
         mColumnSource.setText("Sources, Packages and Archives");\r
 \r
-        Composite composite = new Composite(parent, SWT.NONE);\r
-        composite.setLayoutData(\r
-                new GridData(SWT.FILL, SWT.BEGINNING, false, false, 5, 1));\r
-        GridLayout gl;\r
-        composite.setLayout(gl = new GridLayout(2, false));\r
-        gl.marginHeight = gl.marginWidth = 0;\r
-        // add an empty composite\r
-        Composite spacer = new Composite(composite, SWT.NONE);\r
-        GridData gd;\r
-        spacer.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));\r
-        gd.heightHint = 0;\r
-\r
-        mUpdateOnlyCheckBox = new Button(composite, SWT.CHECK);\r
-        mUpdateOnlyCheckBox.setText("Display updates only");\r
-        mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());\r
-        mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() {\r
-            @Override\r
-            public void widgetSelected(SelectionEvent arg0) {\r
-                onShowUpdateOnly(); //$hide$\r
-            }\r
-        });\r
-\r
         mDescriptionContainer = new Group(parent, SWT.NONE);\r
         mDescriptionContainer.setLayout(new GridLayout(1, false));\r
         mDescriptionContainer.setText("Description");\r
@@ -138,43 +115,59 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
         mDescriptionLabel.setText("Line1\nLine2\nLine3");\r
 \r
         mAddSiteButton = new Button(parent, SWT.NONE);\r
+        mAddSiteButton.setText("Add Site...");\r
+        mAddSiteButton.setToolTipText("Allows you to enter a new user external site. " +\r
+                "Such site can only contribute add-ons and extra packages.");\r
         mAddSiteButton.addSelectionListener(new SelectionAdapter() {\r
             @Override\r
             public void widgetSelected(SelectionEvent e) {\r
                 onAddSiteSelected(); //$hide$\r
             }\r
         });\r
-        mAddSiteButton.setText("Add Site...");\r
 \r
         mDeleteSiteButton = new Button(parent, SWT.NONE);\r
+        mDeleteSiteButton.setText("Delete Site...");\r
+        mDeleteSiteButton.setToolTipText("Allows you to remove an external site. " +\r
+                "Built-in sites cannot be removed.");\r
         mDeleteSiteButton.addSelectionListener(new SelectionAdapter() {\r
             @Override\r
             public void widgetSelected(SelectionEvent e) {\r
                 onRemoveSiteSelected(); //$hide$\r
             }\r
         });\r
-        mDeleteSiteButton.setText("Delete Site...");\r
 \r
-        mPlaceholder3 = new Label(parent, SWT.NONE);\r
-        mPlaceholder3.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));\r
+        mUpdateOnlyCheckBox = new Button(parent, SWT.CHECK);\r
+        mUpdateOnlyCheckBox.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));\r
+        mUpdateOnlyCheckBox.setText("Display updates only");\r
+        mUpdateOnlyCheckBox.setToolTipText("When selected, only compatible update packages are shown in the list above.");\r
+        mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());\r
+        mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent arg0) {\r
+                onShowUpdateOnly(); //$hide$\r
+            }\r
+        });\r
 \r
         mRefreshButton = new Button(parent, SWT.NONE);\r
+        mRefreshButton.setText("Refresh");\r
+        mRefreshButton.setToolTipText("Refreshes the list of packages from open sites.");\r
         mRefreshButton.addSelectionListener(new SelectionAdapter() {\r
             @Override\r
             public void widgetSelected(SelectionEvent e) {\r
                 onRefreshSelected(); //$hide$\r
             }\r
         });\r
-        mRefreshButton.setText("Refresh");\r
 \r
         mInstallSelectedButton = new Button(parent, SWT.NONE);\r
+        mInstallSelectedButton.setText("Install Selected");\r
+        mInstallSelectedButton.setToolTipText("Allows you to review all selected packages " +\r
+                "and install them.");\r
         mInstallSelectedButton.addSelectionListener(new SelectionAdapter() {\r
             @Override\r
             public void widgetSelected(SelectionEvent e) {\r
                 onInstallSelectedArchives();  //$hide$\r
             }\r
         });\r
-        mInstallSelectedButton.setText("Install Selected");\r
     }\r
 \r
     @Override\r
@@ -288,7 +281,9 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
     }\r
 \r
     private void onShowUpdateOnly() {\r
-        mUpdaterData.getSettingsController().setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection());\r
+        SettingsController controller = mUpdaterData.getSettingsController();\r
+        controller.setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection());\r
+        controller.saveSettings();\r
         mTreeViewerSources.refresh();\r
     }\r
 \r
@@ -418,6 +413,9 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
         mDeleteSiteButton.setEnabled(hasSelectedUserSource);\r
         mRefreshButton.setEnabled(true);\r
         mInstallSelectedButton.setEnabled(hasCheckedArchive);\r
+\r
+        // set value on the show only update checkbox\r
+        mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());\r
     }\r
 \r
     // End of hiding from SWT Designer\r
index dbf46f5..de12666 100755 (executable)
@@ -43,6 +43,11 @@ public class RepoSourcesAdapter {
 \r
     private final UpdaterData mUpdaterData;\r
 \r
+    /**\r
+     * A dummy RepoSource entry returned for sources which had load errors.\r
+     * It displays a summary of the error as its short description or\r
+     * it displays the source's long description.\r
+     */\r
     public static class RepoSourceError implements IDescription {\r
 \r
         private final RepoSource mSource;\r
@@ -60,6 +65,33 @@ public class RepoSourcesAdapter {
         }\r
     }\r
 \r
+    /**\r
+     * A dummy RepoSource entry returned for sources with no packages.\r
+     * We need that to force the SWT tree to display an open/close triangle\r
+     * even for empty sources.\r
+     */\r
+    public static class RepoSourceEmpty implements IDescription {\r
+\r
+        private final RepoSource mSource;\r
+        private final boolean mEmptyBecauseOfUpdateOnly;\r
+\r
+        public RepoSourceEmpty(RepoSource source, boolean emptyBecauseOfUpdateOnly) {\r
+            mSource = source;\r
+            mEmptyBecauseOfUpdateOnly = emptyBecauseOfUpdateOnly;\r
+        }\r
+\r
+        public String getLongDescription() {\r
+            return mSource.getLongDescription();\r
+        }\r
+\r
+        public String getShortDescription() {\r
+            if (mEmptyBecauseOfUpdateOnly) {\r
+                return "Some packages were found but are not compatible updates.";\r
+            } else {\r
+                return "No packages found";\r
+            }\r
+        }\r
+    }\r
 \r
     public RepoSourcesAdapter(UpdaterData updaterData) {\r
         mUpdaterData = updaterData;\r
@@ -137,48 +169,76 @@ public class RepoSourcesAdapter {
                 return mUpdaterData.getSources().getSources();\r
 \r
             } else if (parentElement instanceof RepoSource) {\r
-                final RepoSource source = (RepoSource) parentElement;\r
-                Package[] packages = source.getPackages();\r
+                return getRepoSourceChildren((RepoSource) parentElement);\r
+\r
+            } else if (parentElement instanceof Package) {\r
+                return getPackageChildren((Package) parentElement);\r
+            }\r
+\r
+            return new Object[0];\r
+        }\r
 \r
-                if (packages == null && source.getFetchError() == null) {\r
-                    final boolean forceHttp = mUpdaterData.getSettingsController().getForceHttp();\r
+        /**\r
+         * Returns the list of packages for this repo source, eventually filtered to display\r
+         * only update packages. If the list is empty, returns a specific empty node. If there's\r
+         * an error, returns a specific error node.\r
+         */\r
+        private Object[] getRepoSourceChildren(final RepoSource source) {\r
+            Package[] packages = source.getPackages();\r
 \r
-                    mUpdaterData.getTaskFactory().start("Loading Source", new ITask() {\r
-                        public void run(ITaskMonitor monitor) {\r
-                            source.load(monitor, forceHttp);\r
-                        }\r
-                    });\r
+            if (packages == null && source.getFetchError() == null) {\r
+                final boolean forceHttp = mUpdaterData.getSettingsController().getForceHttp();\r
 \r
-                    packages = source.getPackages();\r
-                }\r
-                if (packages != null) {\r
-                    // filter out only the packages that are new/upgrade.\r
-                    if (mUpdaterData.getSettingsController().getShowUpdateOnly()) {\r
-                        return filteredPackages(packages);\r
+                mUpdaterData.getTaskFactory().start("Loading Source", new ITask() {\r
+                    public void run(ITaskMonitor monitor) {\r
+                        source.load(monitor, forceHttp);\r
                     }\r
-                    return packages;\r
-                } else if (source.getFetchError() != null) {\r
-                    // Return a dummy entry to display the fetch error\r
-                    return new Object[] { new RepoSourceError(source) };\r
-                }\r
+                });\r
 \r
-            } else if (parentElement instanceof Package) {\r
-                Archive[] archives = ((Package) parentElement).getArchives();\r
-                if (mUpdaterData.getSettingsController().getShowUpdateOnly()) {\r
-                    for (Archive archive : archives) {\r
-                        // if we only want the compatible archives, then we just take the first\r
-                        // one. it's unlikely there are 2 compatible archives for the same\r
-                        // package\r
-                        if (archive.isCompatible()) {\r
-                            return new Object[] { archive };\r
-                        }\r
+                packages = source.getPackages();\r
+            }\r
+\r
+            boolean wasEmptyBeforeFilter = (packages == null || packages.length == 0);\r
+\r
+            // filter out only the packages that are new/upgrade.\r
+            if (packages != null && mUpdaterData.getSettingsController().getShowUpdateOnly()) {\r
+                packages = filteredPackages(packages);\r
+            }\r
+            if (packages != null && packages.length == 0) {\r
+                packages = null;\r
+            }\r
+\r
+            if (packages != null && source.getFetchError() != null) {\r
+                // Return a dummy entry to display the fetch error\r
+                return new Object[] { new RepoSourceError(source) };\r
+            }\r
+\r
+            // Either return a non-null package list or create a new empty node\r
+            if (packages != null) {\r
+                return packages;\r
+            } else {\r
+                return new Object[] { new RepoSourceEmpty(source, !wasEmptyBeforeFilter) } ;\r
+            }\r
+        }\r
+\r
+        /**\r
+         * Returns the list of archives for the given package, eventually filtering it\r
+         * to only show the compatible archives.\r
+         */\r
+        private Object[] getPackageChildren(Package pkg) {\r
+            Archive[] archives = pkg.getArchives();\r
+            if (mUpdaterData.getSettingsController().getShowUpdateOnly()) {\r
+                for (Archive archive : archives) {\r
+                    // if we only want the compatible archives, then we just take the first\r
+                    // one. it's unlikely there are 2 compatible archives for the same\r
+                    // package\r
+                    if (archive.isCompatible()) {\r
+                        return new Object[] { archive };\r
                     }\r
                 }\r
-\r
-                return archives;\r
             }\r
 \r
-            return new Object[0];\r
+            return archives;\r
         }\r
 \r
         /**\r
@@ -213,7 +273,7 @@ public class RepoSourcesAdapter {
      * @param remotePackages the list of packages to filter.\r
      * @return a non null (but maybe empty) list of new or update packages.\r
      */\r
-    private Object[] filteredPackages(Package[] remotePackages) {\r
+    private Package[] filteredPackages(Package[] remotePackages) {\r
         // get the installed packages\r
         Package[] installedPackages = mUpdaterData.getInstalledPackage();\r
 \r
@@ -233,10 +293,10 @@ public class RepoSourcesAdapter {
                     if (info == UpdateInfo.UPDATE) {\r
                         filteredList.add(remotePkg);\r
                         newPkg = false;\r
-                        break; // there shouldn't be 2 revision of the same package\r
+                        break; // there shouldn't be 2 revisions of the same package\r
                     } else if (info != UpdateInfo.INCOMPATIBLE) {\r
                         newPkg = false;\r
-                        break; // there shouldn't be 2 revision of the same package\r
+                        break; // there shouldn't be 2 revisions of the same package\r
                     }\r
                 }\r
 \r
@@ -247,6 +307,6 @@ public class RepoSourcesAdapter {
             }\r
         }\r
 \r
-        return filteredList.toArray();\r
+        return filteredList.toArray(new Package[filteredList.size()]);\r
     }\r
 }\r
index b6363d3..a4b1a05 100755 (executable)
@@ -26,7 +26,11 @@ import java.io.IOException;
 import java.util.Properties;\r
 \r
 /**\r
- *\r
+ * Controller class to get settings values. Settings are kept in-memory.\r
+ * Users of this class must first load the settings before changing them and save\r
+ * them when modified.\r
+ * <p/>\r
+ * Settings are enumerated by constants in {@link ISettingsPage}.\r
  */\r
 public class SettingsController {\r
 \r
@@ -41,10 +45,30 @@ public class SettingsController {
 \r
     //--- Access to settings ------------\r
 \r
+    /**\r
+     * Returns the value of the ISettingsPage#KEY_FORCE_HTTP setting.\r
+     * @see ISettingsPage#KEY_FORCE_HTTP\r
+     */\r
     public boolean getForceHttp() {\r
         return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP));\r
     }\r
 \r
+    /**\r
+     * Returns the value of the ISettingsPage#KEY_ASK_ADB_RESTART setting.\r
+     * @see ISettingsPage#KEY_ASK_ADB_RESTART\r
+     */\r
+    public boolean getAskBeforeAdbRestart() {\r
+        String value = mProperties.getProperty(ISettingsPage.KEY_ASK_ADB_RESTART);\r
+        if (value == null) {\r
+            return true;\r
+        }\r
+        return Boolean.parseBoolean(value);\r
+    }\r
+\r
+    /**\r
+     * Returns the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.\r
+     * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY\r
+     */\r
     public boolean getShowUpdateOnly() {\r
         String value = mProperties.getProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY);\r
         if (value == null) {\r
@@ -53,8 +77,20 @@ public class SettingsController {
         return Boolean.parseBoolean(value);\r
     }\r
 \r
+    /**\r
+     * Sets the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.\r
+     * @param enabled True if only compatible update items should be shown.\r
+     * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY\r
+     */\r
     public void setShowUpdateOnly(boolean enabled) {\r
-        mProperties.setProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY, Boolean.toString(enabled));\r
+        setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled);\r
+    }\r
+\r
+    /**\r
+     * Internal helper to set a boolean setting.\r
+     */\r
+    private void setSetting(String key, boolean value) {\r
+        mProperties.setProperty(key, Boolean.toString(value));\r
     }\r
 \r
     //--- Controller methods -------------\r
@@ -89,6 +125,10 @@ public class SettingsController {
                 fis = new FileInputStream(f);\r
 \r
                 mProperties.load(fis);\r
+\r
+                // Properly reformat some settings to enforce their default value when missing.\r
+                setShowUpdateOnly(getShowUpdateOnly());\r
+                setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, getAskBeforeAdbRestart());\r
             }\r
 \r
         } catch (AndroidLocationException e) {\r
index 94f68fb..d7d3a90 100755 (executable)
@@ -28,6 +28,8 @@ import org.eclipse.jface.viewers.TableViewer;
 import org.eclipse.jface.viewers.Viewer;\r
 import org.eclipse.swt.SWT;\r
 import org.eclipse.swt.custom.SashForm;\r
+import org.eclipse.swt.custom.StyleRange;\r
+import org.eclipse.swt.custom.StyledText;\r
 import org.eclipse.swt.events.ControlAdapter;\r
 import org.eclipse.swt.events.ControlEvent;\r
 import org.eclipse.swt.events.SelectionAdapter;\r
@@ -48,7 +50,6 @@ import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;\r
 import org.eclipse.swt.widgets.Table;\r
 import org.eclipse.swt.widgets.TableColumn;\r
-import org.eclipse.swt.widgets.Text;\r
 \r
 import java.util.ArrayList;\r
 import java.util.Collection;\r
@@ -88,7 +89,7 @@ final class UpdateChooserDialog extends Dialog {
     private TableViewer mTableViewPackage;\r
     private Table mTablePackage;\r
     private TableColumn mTableColum;\r
-    private Text mPackageText;\r
+    private StyledText mPackageText;\r
     private Button mLicenseRadioAccept;\r
     private Button mLicenseRadioReject;\r
     private Button mLicenseRadioAcceptAll;\r
@@ -99,11 +100,15 @@ final class UpdateChooserDialog extends Dialog {
 \r
     /**\r
      * Create the dialog.\r
+     * @param parentShell The shell to use, typically updaterData.getWindowShell()\r
      * @param updaterData The updater data\r
      * @param newToOldUpdates The map [new archive => old archive] of potential updates\r
      */\r
-    public UpdateChooserDialog(UpdaterData updaterData, Map<Archive, Archive> newToOldUpdates) {\r
-        super(updaterData.getWindowShell(), SWT.APPLICATION_MODAL);\r
+    public UpdateChooserDialog(Shell parentShell,\r
+            UpdaterData updaterData,\r
+            Map<Archive, Archive> newToOldUpdates) {\r
+        super(parentShell,\r
+              SWT.APPLICATION_MODAL);\r
         mUpdaterData = updaterData;\r
 \r
         mNewToOldArchiveMap = new TreeMap<Archive, Archive>(new Comparator<Archive>() {\r
@@ -207,13 +212,13 @@ final class UpdateChooserDialog extends Dialog {
         mPackageTextGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));\r
         mPackageTextGroup.setLayout(new GridLayout(1, false/*makeColumnsEqual*/));\r
 \r
-        mPackageText = new Text(mPackageTextGroup,\r
-                SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL);\r
+        mPackageText = new StyledText(mPackageTextGroup,\r                        SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL);\r
+        mPackageText.setBackground(\r
+                getParent().getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));\r
         mPackageText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));\r
 \r
         mLicenseRadioAccept = new Button(mPackageRootComposite, SWT.RADIO);\r
         mLicenseRadioAccept.setText("Accept");\r
-        mLicenseRadioAccept.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));\r
         mLicenseRadioAccept.addSelectionListener(new SelectionAdapter() {\r
             @Override\r
             public void widgetSelected(SelectionEvent e) {\r
@@ -223,7 +228,6 @@ final class UpdateChooserDialog extends Dialog {
 \r
         mLicenseRadioReject = new Button(mPackageRootComposite, SWT.RADIO);\r
         mLicenseRadioReject.setText("Reject");\r
-        mLicenseRadioReject.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));\r
         mLicenseRadioReject.addSelectionListener(new SelectionAdapter() {\r
             @Override\r
             public void widgetSelected(SelectionEvent e) {\r
@@ -236,8 +240,6 @@ final class UpdateChooserDialog extends Dialog {
 \r
         mLicenseRadioAcceptAll = new Button(mPackageRootComposite, SWT.RADIO);\r
         mLicenseRadioAcceptAll.setText("Accept All");\r
-        mLicenseRadioAcceptAll.setLayoutData(\r
-                new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));\r
         mLicenseRadioAcceptAll.addSelectionListener(new SelectionAdapter() {\r
             @Override\r
             public void widgetSelected(SelectionEvent e) {\r
@@ -317,7 +319,6 @@ final class UpdateChooserDialog extends Dialog {
         onPackageSelected();\r
     }\r
 \r
-\r
     /**\r
      * Creates the icon of the window shell.\r
      */\r
@@ -445,27 +446,46 @@ final class UpdateChooserDialog extends Dialog {
             return;\r
         }\r
 \r
-        StringBuilder sb = new StringBuilder();\r
+        mPackageText.setText("");                                               //$NON-NLS-1$\r
+\r
+        addSectionTitle("Package Description\n");\r
+        addText(a.getParentPackage().getLongDescription(), "\n\n");             //$NON-NLS-1$\r
 \r
         Archive aold = mNewToOldArchiveMap.get(a);\r
         if (aold != null) {\r
-            sb.append("*** Existing Package Description:\n");\r
-            sb.append(aold.getParentPackage().getLongDescription()).append("\n\n");\r
+            addText(String.format("This update will replace revision %1$s with revision %2$s.\n\n",\r
+                    aold.getParentPackage().getRevision(),\r
+                    a.getParentPackage().getRevision()));\r
         }\r
 \r
-        sb.append("*** New Package Description:\n");\r
-        sb.append(a.getParentPackage().getLongDescription()).append("\n\n");\r
 \r
-        sb.append("\n*** Archive Description:\n");\r
-        sb.append(a.getLongDescription()).append("\n");\r
+        addSectionTitle("Archive Description\n");\r
+        addText(a.getLongDescription(), "\n\n");                                //$NON-NLS-1$\r
 \r
         String license = a.getParentPackage().getLicense();\r
         if (license != null) {\r
-            sb.append("\n*** Package License:\n");\r
-            sb.append(license).append("\n");\r
+            addSectionTitle("License\n");\r
+            addText(license.trim(), "\n");                                      //$NON-NLS-1$\r
+        }\r
+    }\r
+\r
+    private void addText(String...string) {\r
+        for (String s : string) {\r
+            mPackageText.append(s);\r
         }\r
+    }\r
 \r
-        mPackageText.setText(sb.toString());\r
+    private void addSectionTitle(String string) {\r
+        String s = mPackageText.getText();\r
+        int start = (s == null ? 0 : s.length());\r
+        mPackageText.append(string);\r
+\r
+        StyleRange sr = new StyleRange();\r
+        sr.start = start;\r
+        sr.length = string.length();\r
+        sr.fontStyle = SWT.BOLD;\r
+        sr.underline = true;\r
+        mPackageText.setStyleRange(sr);\r
     }\r
 \r
     private void updateLicenceRadios(Archive a) {\r
index 321f5ca..bec00f8 100755 (executable)
@@ -34,8 +34,12 @@ import com.android.sdklib.internal.repository.Package.UpdateInfo;
 import com.android.sdkuilib.internal.repository.icons.ImageFactory;\r
 import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;\r
 \r
+import org.eclipse.jface.dialogs.MessageDialog;\r
+import org.eclipse.swt.widgets.Display;\r
 import org.eclipse.swt.widgets.Shell;\r
 \r
+import java.io.ByteArrayOutputStream;\r
+import java.io.PrintStream;\r
 import java.util.ArrayList;\r
 import java.util.Collection;\r
 import java.util.HashMap;\r
@@ -292,20 +296,34 @@ class UpdaterData {
                             break;\r
                         }\r
 \r
-                        if (archive.getParentPackage() instanceof AddonPackage) {\r
-                            installedAddon = true;\r
-                        } else if (archive.getParentPackage() instanceof ToolPackage) {\r
-                            installedTools = true;\r
-                        }\r
-\r
                         if (archive.install(mOsSdkRoot, forceHttp, mSdkManager, monitor)) {\r
                             numInstalled++;\r
+\r
+                            // Check if we successfully installed a tool or add-on package.\r
+                            if (archive.getParentPackage() instanceof AddonPackage) {\r
+                                installedAddon = true;\r
+                            } else if (archive.getParentPackage() instanceof ToolPackage) {\r
+                                installedTools = true;\r
+                            }\r
                         }\r
 \r
                     } catch (Throwable t) {\r
                         // Display anything unexpected in the monitor.\r
-                        monitor.setResult("Unexpected Error: %1$s", t.getMessage());\r
-\r
+                        String msg = t.getMessage();\r
+                        if (msg != null) {\r
+                            monitor.setResult("Unexpected Error installing '%1$s': %2$s",\r
+                                    archive.getParentPackage().getShortDescription(), msg);\r
+                        } else {\r
+                            // no error info? get the stack call to display it\r
+                            // At least that'll give us a better bug report.\r
+                            ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+                            t.printStackTrace(new PrintStream(baos));\r
+\r
+                            // and display it\r
+                            monitor.setResult("Unexpected Error installing '%1$s'\n%2$s",\r
+                                    archive.getParentPackage().getShortDescription(),\r
+                                    baos.toString());\r
+                        }\r
                     } finally {\r
 \r
                         // Always move the progress bar to the desired position.\r
@@ -319,8 +337,10 @@ class UpdaterData {
                     // Update the USB vendor ids for adb\r
                     try {\r
                         mSdkManager.updateAdb();\r
+                        monitor.setResult("Updated ADB to support the USB devices declared in the SDK add-ons.");\r
                     } catch (Exception e) {\r
                         mSdkLog.error(e, "Update ADB failed");\r
+                        monitor.setResult("failed to update adb to support the USB devices declared in the SDK add-ons.");\r
                     }\r
                 }\r
 \r
@@ -331,8 +351,11 @@ class UpdaterData {
                     // before updating the tools folder, as adb.exe is (surprisingly) not\r
                     // locked.\r
 \r
-                    // TODO either bring in ddmlib and use its existing methods to stop adb\r
-                    // or use a shell exec to tools/adb.\r
+                    askForAdbRestart(monitor);\r
+                }\r
+\r
+                if (installedTools) {\r
+                    notifyToolsNeedsToBeRestarted();\r
                 }\r
 \r
                 if (numInstalled == 0) {\r
@@ -350,8 +373,56 @@ class UpdaterData {
     }\r
 \r
     /**\r
+     * Attemps to restart ADB.\r
+     *\r
+     * If the "ask before restart" setting is set (the default), prompt the user whether\r
+     * now is a good time to restart ADB.\r
+     * @param monitor\r
+     */\r
+    private void askForAdbRestart(ITaskMonitor monitor) {\r
+        final boolean[] canRestart = new boolean[] { true };\r
+\r
+        if (getSettingsController().getAskBeforeAdbRestart()) {\r
+            // need to ask for permission first\r
+            Display display = mWindowShell.getDisplay();\r
+\r
+            display.syncExec(new Runnable() {\r
+                public void run() {\r
+                    canRestart[0] = MessageDialog.openQuestion(mWindowShell,\r
+                            "ADB Restart",\r
+                            "A package that depends on ADB has been updated. It is recommended " +\r
+                            "to restart ADB. Is it OK to do it now? If not, you can restart it " +\r
+                            "manually later.");\r
+                }\r
+            });\r
+        }\r
+\r
+        if (canRestart[0]) {\r
+            AdbWrapper adb = new AdbWrapper(getOsSdkRoot(), monitor);\r
+            adb.stopAdb();\r
+            adb.startAdb();\r
+        }\r
+    }\r
+\r
+    private void notifyToolsNeedsToBeRestarted() {\r
+        Display display = mWindowShell.getDisplay();\r
+\r
+        display.syncExec(new Runnable() {\r
+            public void run() {\r
+                MessageDialog.openInformation(mWindowShell,\r
+                        "Android Tools Updated",\r
+                        "The Android SDK tool that you are currently using has been updated. " +\r
+                        "It is recommended that you now close the Android SDK window and re-open it. " +\r
+                        "If you started this window from Eclipse, please check if the Android " +\r
+                        "plug-in needs to be updated.");\r
+            }\r
+        });\r
+    }\r
+\r
+\r
+    /**\r
      * Tries to update all the *existing* local packages.\r
-     * This first refreshes all sources, then compares the available remote packages when\r
+     * This first refreshes all sources, then compares the available remote packages with\r
      * the current local ones and suggest updates to be done to the user. Finally all\r
      * selected updates are installed.\r
      *\r
@@ -371,13 +442,13 @@ class UpdaterData {
             // selected archives. If they do not match an update, list them anyway\r
             // except they map themselves to null (no "old" archive)\r
             for (Archive a : selectedArchives) {\r
-                if (!updates.containsValue(a)) {\r
+                if (!updates.containsKey(a)) {\r
                     updates.put(a, null);\r
                 }\r
             }\r
         }\r
 \r
-        UpdateChooserDialog dialog = new UpdateChooserDialog(this, updates);\r
+        UpdateChooserDialog dialog = new UpdateChooserDialog(getWindowShell(), this, updates);\r
         dialog.open();\r
 \r
         Collection<Archive> result = dialog.getResult();\r
@@ -429,9 +500,9 @@ class UpdaterData {
         // Map [remote archive => local archive] of suitable update candidates\r
         Map<Archive, Archive> result = new HashMap<Archive, Archive>();\r
 \r
-        // First go thru all sources and make a local list of all available archives\r
+        // First go thru all sources and make a list of all available remote archives\r
         // sorted by package class.\r
-        HashMap<Class<? extends Package>, ArrayList<Archive>> availPkgs =\r
+        HashMap<Class<? extends Package>, ArrayList<Archive>> availablePkgs =\r
             new HashMap<Class<? extends Package>, ArrayList<Archive>>();\r
 \r
         if (selectedArchives != null) {\r
@@ -442,9 +513,9 @@ class UpdaterData {
                 if (a.isCompatible()) {\r
                     Class<? extends Package> clazz = a.getParentPackage().getClass();\r
 \r
-                    ArrayList<Archive> list = availPkgs.get(clazz);\r
+                    ArrayList<Archive> list = availablePkgs.get(clazz);\r
                     if (list == null) {\r
-                        availPkgs.put(clazz, list = new ArrayList<Archive>());\r
+                        availablePkgs.put(clazz, list = new ArrayList<Archive>());\r
                     }\r
 \r
                     list.add(a);\r
@@ -461,9 +532,9 @@ class UpdaterData {
                     for (Package remotePkg : remotePkgs) {\r
                         Class<? extends Package> clazz = remotePkg.getClass();\r
 \r
-                        ArrayList<Archive> list = availPkgs.get(clazz);\r
+                        ArrayList<Archive> list = availablePkgs.get(clazz);\r
                         if (list == null) {\r
-                            availPkgs.put(clazz, list = new ArrayList<Archive>());\r
+                            availablePkgs.put(clazz, list = new ArrayList<Archive>());\r
                         }\r
 \r
                         for (Archive a : remotePkg.getArchives()) {\r
@@ -485,7 +556,7 @@ class UpdaterData {
 \r
         for (Package localPkg : localPkgs) {\r
             // get the available archive list for this package type\r
-            ArrayList<Archive> list = availPkgs.get(localPkg.getClass());\r
+            ArrayList<Archive> list = availablePkgs.get(localPkg.getClass());\r
 \r
             // if this list is empty, we'll never find anything that matches\r
             if (list == null || list.size() == 0) {\r
@@ -496,7 +567,7 @@ class UpdaterData {
             Archive[] localArchives = localPkg.getArchives();\r
             if (localArchives != null && localArchives.length > 0) {\r
                 Archive localArchive = localArchives[0];\r
-                // only consider archive compatible with the current platform\r
+                // only consider archives compatible with the current platform\r
                 if (localArchive != null && localArchive.isCompatible()) {\r
 \r
                     // We checked all this archive stuff because that's what eventually gets\r
index 0fbcc41..b9cf0a4 100755 (executable)
@@ -109,7 +109,7 @@ public class UpdaterWindowImpl {
      * Create contents of the window.\r
      */\r
     protected void createContents() {\r
-        mAndroidSdkUpdater = new Shell(mParentShell);\r
+        mAndroidSdkUpdater = new Shell(mParentShell, SWT.SHELL_TRIM);\r
         mAndroidSdkUpdater.addDisposeListener(new DisposeListener() {\r
             public void widgetDisposed(DisposeEvent e) {\r
                 onAndroidSdkUpdaterDispose();    //$hide$ (hide from SWT designer)\r
@@ -335,17 +335,40 @@ public class UpdaterWindowImpl {
         RepoSources sources = mUpdaterData.getSources();\r
         sources.add(new RepoSource(SdkRepository.URL_GOOGLE_SDK_REPO_SITE, false /*userSource*/));\r
 \r
-        String str = System.getenv("TEMP_SDK_URL"); // TODO STOPSHIP temporary remove before shipping\r
+        // SDK_UPDATER_URLS is a semicolon-separated list of URLs that can be used to\r
+        // seed the SDK Updater list for full repositories.\r
+        String str = System.getenv("SDK_UPDATER_URLS");\r
         if (str != null) {\r
             String[] urls = str.split(";");\r
             for (String url : urls) {\r
-                sources.add(new RepoSource(url, false /*userSource*/));\r
+                if (url != null && url.length() > 0) {\r
+                    RepoSource s = new RepoSource(url, false /*userSource*/);\r
+                    if (!sources.hasSource(s)) {\r
+                        sources.add(s);\r
+                    }\r
+                }\r
             }\r
         }\r
 \r
         // Load user sources\r
         sources.loadUserSources(mUpdaterData.getSdkLog());\r
 \r
+        // SDK_UPDATER_USER_URLS is a semicolon-separated list of URLs that can be used to\r
+        // seed the SDK Updater list for user-only repositories. User sources can only provide\r
+        // add-ons and extra packages.\r
+        str = System.getenv("SDK_UPDATER_USER_URLS");\r
+        if (str != null) {\r
+            String[] urls = str.split(";");\r
+            for (String url : urls) {\r
+                if (url != null && url.length() > 0) {\r
+                    RepoSource s = new RepoSource(url, true /*userSource*/);\r
+                    if (!sources.hasSource(s)) {\r
+                        sources.add(s);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
         mRemotePackagesPage.onSdkChange();\r
     }\r
 \r
index bd39c5e..cbd846e 100755 (executable)
@@ -91,31 +91,34 @@ public class ImageFactory {
      */\r
     public Image getImageForObject(Object object) {\r
         if (object instanceof RepoSource) {\r
-            return getImageByName("source_icon16.png");\r
+            return getImageByName("source_icon16.png");                         //$NON-NLS-1$\r
 \r
         } else if (object instanceof RepoSourcesAdapter.RepoSourceError) {\r
-            return getImageByName("error_icon16.png");\r
+            return getImageByName("error_icon16.png");                          //$NON-NLS-1$\r
+\r
+        } else if (object instanceof RepoSourcesAdapter.RepoSourceEmpty) {\r
+            return getImageByName("nopkg_icon16.png");                          //$NON-NLS-1$\r
 \r
         } else if (object instanceof PlatformPackage) {\r
-            return getImageByName("android_icon_16.png");\r
+            return getImageByName("android_icon_16.png");                       //$NON-NLS-1$\r
 \r
         } else if (object instanceof AddonPackage) {\r
-            return getImageByName("addon_icon16.png");\r
+            return getImageByName("addon_icon16.png");                          //$NON-NLS-1$\r
 \r
         } else if (object instanceof ToolPackage) {\r
-            return getImageByName("tool_icon16.png");\r
+            return getImageByName("tool_icon16.png");                           //$NON-NLS-1$\r
 \r
         } else if (object instanceof DocPackage) {\r
-            return getImageByName("doc_icon16.png");\r
+            return getImageByName("doc_icon16.png");                            //$NON-NLS-1$\r
 \r
         } else if (object instanceof ExtraPackage) {\r
-            return getImageByName("extra_icon16.png");\r
+            return getImageByName("extra_icon16.png");                          //$NON-NLS-1$\r
 \r
         } else if (object instanceof Archive) {\r
             if (((Archive) object).isCompatible()) {\r
-                return getImageByName("archive_icon16.png");\r
+                return getImageByName("archive_icon16.png");                    //$NON-NLS-1$\r
             } else {\r
-                return getImageByName("incompat_icon16.png");\r
+                return getImageByName("incompat_icon16.png");                   //$NON-NLS-1$\r
             }\r
         }\r
         return null;\r
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png
new file mode 100755 (executable)
index 0000000..147837f
Binary files /dev/null and b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png differ
index a85c1b8..ba1bb4c 100644 (file)
@@ -81,7 +81,7 @@ public final class AvdSelector {
     private Button mNewButton;
     private Button mRefreshButton;
     private Button mManagerButton;
-    private Button mUpdateButton;
+    private Button mRepairButton;
     private Button mStartButton;
 
     private SelectionListener mSelectionListener;
@@ -256,14 +256,14 @@ public final class AvdSelector {
                 }
             });
 
-            mUpdateButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
-            mUpdateButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-            mUpdateButton.setText("Update...");
-            mUpdateButton.setToolTipText("Updates the path of the selected AVD.");
-            mUpdateButton.addSelectionListener(new SelectionAdapter() {
+            mRepairButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
+            mRepairButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+            mRepairButton.setText("Repair...");
+            mRepairButton.setToolTipText("Repairs the selected AVD.");
+            mRepairButton.addSelectionListener(new SelectionAdapter() {
                 @Override
                 public void widgetSelected(SelectionEvent arg0) {
-                    onUpdate();
+                    onRepair();
                 }
             });
 
@@ -298,8 +298,8 @@ public final class AvdSelector {
 
         mRefreshButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
         mRefreshButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-        mRefreshButton.setText("Resfresh");
-        mRefreshButton.setToolTipText("Reloads the list of AVD.\nUse this if you create AVD from the command line.");
+        mRefreshButton.setText("Refresh");
+        mRefreshButton.setToolTipText("Reloads the list of AVD.\nUse this if you create AVDs from the command line.");
         mRefreshButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent arg0) {
@@ -775,8 +775,8 @@ public final class AvdSelector {
             if (mDeleteButton != null) {
                 mDeleteButton.setEnabled(false);
             }
-            if (mUpdateButton != null) {
-                mUpdateButton.setEnabled(false);
+            if (mRepairButton != null) {
+                mRepairButton.setEnabled(false);
             }
         } else {
             AvdInfo selection = getTableSelection();
@@ -790,8 +790,8 @@ public final class AvdSelector {
             if (mDeleteButton != null) {
                 mDeleteButton.setEnabled(hasSelection);
             }
-            if (mUpdateButton != null) {
-                mUpdateButton.setEnabled(hasSelection &&
+            if (mRepairButton != null) {
+                mRepairButton.setEnabled(hasSelection &&
                         selection.getStatus() == AvdStatus.ERROR_IMAGE_DIR);
             }
         }
@@ -851,7 +851,12 @@ public final class AvdSelector {
         }
     }
 
-    private void onUpdate() {
+    /**
+     * Repairs the selected AVD.
+     * <p/>
+     * For now this only supports fixing the wrong value in image.sysdir.*
+     */
+    private void onRepair() {
         final AvdInfo avdInfo = getTableSelection();
 
         // get the current Display
index 688474e..c98fc94 100644 (file)
@@ -44,6 +44,8 @@ import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URLEncoder;
 import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /** Utility class to send "ping" usage reports to the server. */
 public class SdkStatsService {
@@ -93,14 +95,14 @@ public class SdkStatsService {
         "kfmclient openURL %URL%",                    // $NON-NLS-1$ Konqueror
         "opera -newwindow %URL%",                     // $NON-NLS-1$ Opera
     };
-    
+
     public final static String PING_OPT_IN = "pingOptIn"; //$NON-NLS-1$
     public final static String PING_TIME = "pingTime"; //$NON-NLS-1$
     public final static String PING_ID = "pingId"; //$NON-NLS-1$
 
 
     private static PreferenceStore sPrefStore;
-    
+
     /**
      * Send a "ping" to the Google toolbar server, if enough time has
      * elapsed since the last ping, and if the user has not opted out.
@@ -123,7 +125,7 @@ public class SdkStatsService {
             if (!prefs.contains(PING_ID)) {
                 // First time: make up a new ID.  TODO: Use something more random?
                 prefs.setValue(PING_ID, new Random().nextLong());
-    
+
                 // Also give them a chance to opt out.
                 prefs.setValue(PING_OPT_IN, getUserPermission(display));
                 try {
@@ -132,13 +134,13 @@ public class SdkStatsService {
                 catch (IOException ioe) {
                 }
             }
-    
+
             // If the user has not opted in, do nothing and quietly return.
             if (!prefs.getBoolean(PING_OPT_IN)) {
                 // user opted out.
                 return;
             }
-    
+
             // If the last ping *for this app* was too recent, do nothing.
             String timePref = PING_TIME + "." + app;  // $NON-NLS-1$
             long now = System.currentTimeMillis();
@@ -147,7 +149,7 @@ public class SdkStatsService {
                 // too soon after a ping.
                 return;
             }
-    
+
             // Record the time of the attempt, whether or not it succeeds.
             prefs.setValue(timePref, now);
             try {
@@ -155,7 +157,7 @@ public class SdkStatsService {
             }
             catch (IOException ioe) {
             }
-    
+
             // Send the ping itself in the background (don't block if the
             // network is down or slow or confused).
             final long id = prefs.getLong(PING_ID);
@@ -171,7 +173,7 @@ public class SdkStatsService {
             }.start();
         }
     }
-    
+
     /**
      * Returns the DDMS {@link PreferenceStore}.
      */
@@ -187,7 +189,7 @@ public class SdkStatsService {
 
             if (homeDir != null) {
                 String rcFileName = homeDir + "ddms.cfg"; //$NON-NLS-1$
-                
+
                 // also look for an old pref file in the previous location
                 String oldPrefPath = System.getProperty("user.home") //$NON-NLS-1$
                     + File.separator + ".ddmsrc"; //$NON-NLS-1$
@@ -196,10 +198,10 @@ public class SdkStatsService {
                     try {
                         PreferenceStore oldStore = new PreferenceStore(oldPrefPath);
                         oldStore.load();
-                        
+
                         oldStore.save(new FileOutputStream(rcFileName), "");
                         oldPrefFile.delete();
-                        
+
                         PreferenceStore newStore = new PreferenceStore(rcFileName);
                         newStore.load();
                         sPrefStore = newStore;
@@ -220,10 +222,10 @@ public class SdkStatsService {
                 sPrefStore = new PreferenceStore();
             }
         }
-        
+
         return sPrefStore;
     }
-    
+
     /**
      * Unconditionally send a "ping" request to the Google toolbar server.
      *
@@ -239,8 +241,16 @@ public class SdkStatsService {
         String os = System.getProperty("os.name");          // $NON-NLS-1$
         if (os.startsWith("Mac OS")) {                      // $NON-NLS-1$
             os = "mac";                                     // $NON-NLS-1$
+            String osVers = getVersion();
+            if (osVers != null) {
+                os = os + "-" + osVers;                     // $NON-NLS-1$
+            }
         } else if (os.startsWith("Windows")) {              // $NON-NLS-1$
             os = "win";                                     // $NON-NLS-1$
+            String osVers = getVersion();
+            if (osVers != null) {
+                os = os + "-" + osVers;                     // $NON-NLS-1$
+            }
         } else if (os.startsWith("Linux")) {                // $NON-NLS-1$
             os = "linux";                                   // $NON-NLS-1$
         } else {
@@ -272,6 +282,24 @@ public class SdkStatsService {
     }
 
     /**
+     * Returns the version of the os if it is defined as X.Y, or null otherwise.
+     * <p/>
+     * Example of returned versions can be found at http://lopica.sourceforge.net/os.html
+     * <p/>
+     * This method removes any exiting micro versions.
+     */
+    private static String getVersion() {
+        Pattern p = Pattern.compile("(\\d+)\\.(\\d+).*"); // $NON-NLS-1$
+        String osVers = System.getProperty("os.version"); // $NON-NLS-1$
+        Matcher m = p.matcher(osVers);
+        if (m.matches()) {
+            return m.group(1) + "." + m.group(2);         // $NON-NLS-1$
+        }
+
+        return null;
+    }
+
+    /**
      * Prompt the user for whether they want to opt out of reporting.
      * @return whether the user allows reporting (they do not opt out).
      */