aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--androidprefs/Android.mk12
-rw-r--r--androidprefs/NOTICE190
-rw-r--r--anttasks/.classpath1
-rw-r--r--anttasks/Android.mk18
-rw-r--r--anttasks/NOTICE190
-rw-r--r--anttasks/etc/manifest.txt2
-rw-r--r--anttasks/src/com/android/ant/SetupTask.java4
-rw-r--r--apkbuilder/NOTICE190
-rw-r--r--archquery/Android.mk15
-rw-r--r--archquery/NOTICE190
-rwxr-xr-xbuild/patch_windows_sdk.sh3
-rw-r--r--build/tools.atree14
-rw-r--r--build/windows_sdk_tools.mk2
-rw-r--r--changes.txt8
-rw-r--r--common/Android.mk3
-rw-r--r--common/NOTICE190
-rw-r--r--common/src/com/android/AndroidConstants.java50
-rw-r--r--common/src/com/android/io/FileWrapper.java (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java)6
-rw-r--r--common/src/com/android/io/FolderWrapper.java (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java)2
-rw-r--r--common/src/com/android/io/IAbstractFile.java (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java)7
-rw-r--r--common/src/com/android/io/IAbstractFolder.java (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java)2
-rw-r--r--common/src/com/android/io/IAbstractResource.java (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java)2
-rw-r--r--common/src/com/android/io/StreamException.java (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/io/StreamException.java)2
-rw-r--r--common/src/com/android/resources/FolderTypeRelationship.java164
-rw-r--r--common/src/com/android/resources/ResourceFolderType.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolderType.java)33
-rw-r--r--common/src/com/android/resources/ResourceType.java4
-rw-r--r--common/tests/.classpath8
-rw-r--r--common/tests/.project17
-rw-r--r--common/tests/Android.mk (renamed from archquery/src/Android.mk)16
-rw-r--r--common/tests/src/com/android/resources/FolderTypeRelationShipTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/manager/FolderTypeRelationShipTest.java)13
-rw-r--r--ddms/app/.classpath5
-rw-r--r--ddms/app/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--ddms/app/Android.mk31
-rw-r--r--ddms/app/NOTICE190
-rw-r--r--ddms/app/README78
-rwxr-xr-xddms/app/etc/ddms10
-rwxr-xr-xddms/app/etc/ddms.bat2
-rw-r--r--ddms/app/etc/manifest.txt2
-rw-r--r--ddms/app/src/Android.mk26
-rw-r--r--ddms/app/src/com/android/ddms/AboutDialog.java2
-rw-r--r--ddms/app/src/com/android/ddms/PrefsDialog.java6
-rw-r--r--ddms/app/src/com/android/ddms/UIThread.java176
-rw-r--r--ddms/app/src/images/ddms-128.pngbin0 -> 17692 bytes
-rw-r--r--ddms/app/src/resources/images/ddms-icon.pngbin3633 -> 0 bytes
-rw-r--r--ddms/app/src/resources/images/ddms-logo.pngbin3633 -> 0 bytes
-rw-r--r--ddms/libs/ddmlib/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--ddms/libs/ddmlib/Android.mk1
-rw-r--r--ddms/libs/ddmlib/NOTICE190
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java4
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java4
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/SyncException.java2
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java84
-rw-r--r--ddms/libs/ddmuilib/.classpath3
-rw-r--r--ddms/libs/ddmuilib/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--ddms/libs/ddmuilib/Android.mk28
-rw-r--r--ddms/libs/ddmuilib/NOTICE190
-rw-r--r--ddms/libs/ddmuilib/src/Android.mk27
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/ImageLoader.java9
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/ScreenShotDialog.java19
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/explorer/DeviceExplorer.java97
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java6
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/EditFilterDialog.java74
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogFilter.java23
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java6
-rw-r--r--ddms/libs/ddmuilib/src/images/add.png (renamed from ddms/libs/ddmuilib/src/resources/images/add.png)bin146 -> 146 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/android.png (renamed from ddms/libs/ddmuilib/src/resources/images/android.png)bin3609 -> 3609 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/backward.png (renamed from ddms/libs/ddmuilib/src/resources/images/backward.png)bin136 -> 136 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/capture.png (renamed from ddms/libs/ddmuilib/src/resources/images/capture.png)bin691 -> 691 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/clear.png (renamed from ddms/libs/ddmuilib/src/resources/images/clear.png)bin217 -> 217 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/d.png (renamed from ddms/libs/ddmuilib/src/resources/images/d.png)bin638 -> 638 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/debug-attach.png (renamed from ddms/libs/ddmuilib/src/resources/images/debug-attach.png)bin156 -> 156 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/debug-error.png (renamed from ddms/libs/ddmuilib/src/resources/images/debug-error.png)bin222 -> 222 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/debug-wait.png (renamed from ddms/libs/ddmuilib/src/resources/images/debug-wait.png)bin156 -> 156 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/delete.png (renamed from ddms/libs/ddmuilib/src/resources/images/delete.png)bin107 -> 107 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/device.png (renamed from ddms/libs/ddmuilib/src/resources/images/device.png)bin135 -> 135 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/down.png (renamed from ddms/libs/ddmuilib/src/resources/images/down.png)bin141 -> 141 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/e.png (renamed from ddms/libs/ddmuilib/src/resources/images/e.png)bin511 -> 511 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/edit.png (renamed from ddms/libs/ddmuilib/src/resources/images/edit.png)bin223 -> 223 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/empty.png (renamed from ddms/libs/ddmuilib/src/resources/images/empty.png)bin75 -> 75 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/emulator.png (renamed from ddms/libs/ddmuilib/src/resources/images/emulator.png)bin287 -> 287 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/file.png (renamed from ddms/libs/ddmuilib/src/resources/images/file.png)bin157 -> 157 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/folder.png (renamed from ddms/libs/ddmuilib/src/resources/images/folder.png)bin123 -> 123 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/forward.png (renamed from ddms/libs/ddmuilib/src/resources/images/forward.png)bin137 -> 137 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/gc.png (renamed from ddms/libs/ddmuilib/src/resources/images/gc.png)bin165 -> 165 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/halt.png (renamed from ddms/libs/ddmuilib/src/resources/images/halt.png)bin197 -> 197 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/heap.png (renamed from ddms/libs/ddmuilib/src/resources/images/heap.png)bin222 -> 222 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/hprof.png (renamed from ddms/libs/ddmuilib/src/resources/images/hprof.png)bin317 -> 317 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/i.png (renamed from ddms/libs/ddmuilib/src/resources/images/i.png)bin498 -> 498 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/importBug.png (renamed from ddms/libs/ddmuilib/src/resources/images/importBug.png)bin191 -> 191 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/load.png (renamed from ddms/libs/ddmuilib/src/resources/images/load.png)bin163 -> 163 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/pause.png (renamed from ddms/libs/ddmuilib/src/resources/images/pause.png)bin98 -> 98 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/play.png (renamed from ddms/libs/ddmuilib/src/resources/images/play.png)bin138 -> 138 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/pull.png (renamed from ddms/libs/ddmuilib/src/resources/images/pull.png)bin329 -> 329 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/push.png (renamed from ddms/libs/ddmuilib/src/resources/images/push.png)bin228 -> 228 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/save.png (renamed from ddms/libs/ddmuilib/src/resources/images/save.png)bin240 -> 240 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/sort_down.png (renamed from ddms/libs/ddmuilib/src/resources/images/sort_down.png)bin102 -> 102 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/sort_up.png (renamed from ddms/libs/ddmuilib/src/resources/images/sort_up.png)bin105 -> 105 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/thread.png (renamed from ddms/libs/ddmuilib/src/resources/images/thread.png)bin121 -> 121 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/tracing_start.png (renamed from ddms/libs/ddmuilib/src/resources/images/tracing_start.png)bin227 -> 227 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/tracing_stop.png (renamed from ddms/libs/ddmuilib/src/resources/images/tracing_stop.png)bin217 -> 217 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/up.png (renamed from ddms/libs/ddmuilib/src/resources/images/up.png)bin134 -> 134 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/v.png (renamed from ddms/libs/ddmuilib/src/resources/images/v.png)bin587 -> 587 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/w.png (renamed from ddms/libs/ddmuilib/src/resources/images/w.png)bin681 -> 681 bytes
-rw-r--r--ddms/libs/ddmuilib/src/images/warning.png (renamed from ddms/libs/ddmuilib/src/resources/images/warning.png)bin147 -> 147 bytes
-rw-r--r--draw9patch/Android.mk20
-rw-r--r--draw9patch/NOTICE190
-rw-r--r--draw9patch/src/com/android/draw9patch/ui/MainFrame.java7
-rw-r--r--draw9patch/src/images/checker.png (renamed from draw9patch/src/resources/images/checker.png)bin1889 -> 1889 bytes
-rw-r--r--draw9patch/src/images/drop.png (renamed from draw9patch/src/resources/images/drop.png)bin5479 -> 5479 bytes
-rw-r--r--dumpeventlog/NOTICE190
-rw-r--r--eclipse/changes.txt102
-rw-r--r--eclipse/dictionary.txt21
-rw-r--r--eclipse/features/com.android.ide.eclipse.adt/feature.xml6
-rw-r--r--eclipse/features/com.android.ide.eclipse.ddms/feature.xml6
-rw-r--r--eclipse/features/com.android.ide.eclipse.hierarchyviewer/feature.xml6
-rw-r--r--eclipse/features/com.android.ide.eclipse.pdt/feature.xml2
-rw-r--r--eclipse/features/com.android.ide.eclipse.tests/feature.xml2
-rw-r--r--eclipse/features/com.android.ide.eclipse.traceview/feature.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/about.ini2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/SearchView.pngbin3338 -> 795 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/VerticalLinearLayout.pngbin0 -> 369 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/android-32.pngbin0 -> 2446 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/android-64.pngbin0 -> 6502 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/android_32x32.pngbin1390 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/android_large.pngbin1447 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/attribute.pngbin0 -> 370 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/draw9patch-16.pngbin0 -> 1826 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/element.pngbin0 -> 449 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/refresh.pngbin0 -> 377 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.pngbin0 -> 528 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/icons/view_menu.pngbin0 -> 205 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml188
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/DrawingStyle.java6
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java19
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/INode.java9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java17
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java21
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/CalendarViewRule.java42
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java120
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java27
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java36
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IncludeRule.java44
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java71
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java5
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertySettingNodeHandler.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RadioGroupRule.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java36
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SlidingDrawerRule.java2
-rw-r--r--[-rwxr-xr-x]eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java112
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java21
-rw-r--r--[-rwxr-xr-x]eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/WebViewRule.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomButtonRule.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addrow.pngbin0 -> 442 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removerow.pngbin0 -> 442 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttrsXmlParser.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java)5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java187
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/ConvertToAndroidAction.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java250
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFix.java408
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AidlProcessor.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java33
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/BaseBuilder.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java54
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerDeltaVisitor.java10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java92
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java18
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ResourceManagerBuilder.java17
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java852
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidSourceViewerConfig.java94
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java118
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimDescriptors.java124
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationContentAssist.java167
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationEditor.java181
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationSourceViewerConfig.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IIdResourceItem.java)20
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimatorDescriptors.java182
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/binaryxml/BinaryXMLDescriber.java31
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorContentAssist.java31
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorDescriptors.java94
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorEditor.java123
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorSourceViewerConfig.java30
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/AttributeDescriptor.java24
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/BooleanAttributeDescriptor.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java108
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java53
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableContentAssist.java31
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableDescriptors.java300
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableEditor.java157
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableSourceViewerConfig.java30
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/AbstractPropertiesFieldsPart.java7
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportEditor.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ContextPullParser.java21
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java44
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java24
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/MatchingStrategy.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java184
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/WidgetPullParser.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigEditDialog.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java796
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java58
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java38
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/ViewElementDescriptor.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/AccordionControl.java9
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java77
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPoint.java15
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CustomViewFinder.java377
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java638
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java56
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ExtractIncludeAction.java837
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java228
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/HoverOverlay.java67
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageControl.java15
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java119
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java59
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java104
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java366
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ListViewTypeMenu.java218
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java32
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java24
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java311
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PlayAnimationMenu.java9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java32
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java32
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java296
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java79
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java119
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java53
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepository.java346
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml394
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/rendering-configs.xml178
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutAction.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutContribution.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoring.java508
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutWizard.java156
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewAction.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewContribution.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoring.java283
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewWizard.java193
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeAction.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeContribution.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java642
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeWizard.java125
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java544
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java424
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java233
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RelativeLayoutConversionHelper.java1668
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java1281
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringAction.java183
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringWizard.java75
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInAction.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInContribution.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoring.java423
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInWizard.java267
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestContentAssist.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestEditor.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfo.java559
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiManifestElementNode.java15
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewLinksPart.java21
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/MenuEditor.java64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/descriptors/MenuDescriptors.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesContentAssist.java173
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesEditor.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesTreePage.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java6
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/DecorComposite.java2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/IDecorContent.java11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java31
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java24
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java24
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiListAttributeNode.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java160
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java494
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/XmlEditor.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/descriptors/XmlDescriptors.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java4
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigDelegate.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigurationTab.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/InstrumentationRunnerValidator.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java27
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestHelper.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidNature.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java29
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java34
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/XmlErrorHandler.java14
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidDocumentChange.java2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidPackageRenameParticipant.java7
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeMoveParticipant.java7
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeRenameParticipant.java7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/RefactoringUtil.java43
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringAction.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java17
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringRefactoring.java14
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/renamepackage/ApplicationPackageNameRefactoring.java20
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/CyclicDependencyValidator.java61
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IResourceRepository.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java426
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceItem.java48
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ResourceNameValidator.java)60
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ConfigurableResourceItem.java82
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FolderTypeRelationship.java177
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdResourceItem.java54
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResourceItem.java91
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java949
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Resource.java46
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java335
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidJarLoader.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java77
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java131
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/FrameworkResourceRepository.java76
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java26
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceHandler.java26
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java12
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sourcelookup/AdtSourceLookupDirector.java34
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java21
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java329
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java52
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java18
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java44
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ProjectCheckPage.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java173
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java165
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/WorkingSetGroup.java109
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/WorkingSetHelper.java130
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java182
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java131
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFileWrapper.java10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFolderWrapper.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/templates/activity.template4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/templates/java_file.template2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/about.ini2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/icons/android_32x32.pngbin1390 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/icons/ddms-16.pngbin0 -> 1894 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/icons/ddms-32.pngbin0 -> 2244 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator-16.pngbin0 -> 1766 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator.pngbin287 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/plugin.xml4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/FileExplorerView.java16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/META-INF/MANIFEST.MF2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/about.ini2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/android_32x32.pngbin1390 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/hierarchyviewer-16.pngbin0 -> 1831 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/hierarchyviewer-32.pngbin0 -> 2268 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/plugin.xml2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.pdt/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.pdt/META-INF/MANIFEST.MF2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptParserTest.java200
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFixTest.java283
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssistTest.java836
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadataTest.java191
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java744
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoringTest.java85
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoringTest.java59
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoringTest.java146
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoringTest.java230
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistantTest.java127
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringTest.java299
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoringTest.java59
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror1.xml12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror2.xml9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror3.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror4.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror5.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror6.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror7.xml10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion53.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion54.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion55.txt16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion56.txt14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion57.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1.xml20
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion58.txt4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion59.txt10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion60.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion61.txt18
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1.xml27
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1-expected-applyCompletion15.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1-expected-completion20.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1.xml12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2-expected-applyCompletion16.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2-expected-completion21.txt5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2.xml12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3-expected-applyCompletion14.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3-expected-completion19.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3.xml12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion45.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion46a.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion46b.txt16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-actual-applyCompletion1.xml19
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion1.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion10.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion11a.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion11b.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion12.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion2.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion3.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion39.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion4.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion5.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion6.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion7a.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion7b.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion8.diff2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion9.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion1.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion10.txt10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion11.txt60
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion12.txt60
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion2.txt4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion3.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion39.txt142
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion4.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion5.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion6.txt22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion7a.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion7b.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion8.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion9.txt4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1.xml19
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13a.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13b.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13c.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13a.txt13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13b.txt4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13c.txt12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2.xml5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3-expected-applyCompletion17.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3-expected-applyCompletion18.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3.xml5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion4-expected-completion22.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion4.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion19.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion20.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion21.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion40.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-completion40.txt142
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5.xml23
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion6-expected-applyCompletion22.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion6.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion7-expected-applyCompletion23.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion7.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-applyCompletion41.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion41.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion42.txt4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion43.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion44.txt10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8.xml10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion24a.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion24b.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion25.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion26.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion27.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion28.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion29.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion30.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion31.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion32.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion33.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion34.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion35.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion36.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion37.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion38.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion23.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion24.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion25.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion26.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion27.txt13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion28.txt13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion29.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion30.txt8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion31.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion32.txt293
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion33.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion34.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion35.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion36.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion37.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion38.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1.xml20
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion47.txt13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion48.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion49.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion50.txt13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1.xml5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2-expected-completion51.txt10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2-expected-completion52.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2.xml5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1b.diff3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1c.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1d.diff6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract2.diff6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract3.diff15
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract4.diff7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract5.diff8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract6.diff6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract8.diff4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.info3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2-expected-extract7.diff13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.info3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion14.txt10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion15.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion16.txt14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion17.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion18.txt27
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate10.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate11a.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate11g.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate9a.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest.xml19
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/metadata.xml10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate1.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate12.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate2.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate3.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate4.txt89
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-extract2.diff6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate5.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate6.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate7.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate8.txt7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles.xml28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout1-expected-extract1.xml2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout2-expected-extract2.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract3.xml2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract4.xml2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract5.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout6-expected-extract6.diff11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles-expected-extract1.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles2-expected-extract1b.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract1c.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract8.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract1d.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract3.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles5-expected-extract4.diff8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles6-expected-extract5.diff8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles7-expected-extract6.diff13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles8-expected-extract7.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix1.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix2.xml4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix3.xml8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1.xml16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix2-expected-quickFix4.xml3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix2.xml5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant1.txt3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant2.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant3.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant4.txt2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-changeLayout1a.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-changeView1.xml21
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-extract6.diff9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a.info13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a.xml21
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b-expected-changeLayout1b.xml45
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b.info14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b.xml70
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-changeLayout2.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-changeView2.xml13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-extract3.xml13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2.info9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2.xml13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-changeLayout3.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract1.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract2.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract4.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract5.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn1.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn2.xml12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn3.xml11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1-expected-extract4.xml9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1-expected-extract5.xml8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1.xml10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2-expected-extract4.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2-expected-extract5.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2.xml8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3.info5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4-expected-changeLayout4.xml6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4.info5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4.xml8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5-expected-changeLayout5.xml10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5.info9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5.xml12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6-expected-changeLayout6.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6.info5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7-expected-extract6.diff10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7.info11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7.xml19
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8-expected-extract6.diff15
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8.info12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8.xml40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfoTest.java328
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/xml/HyperlinksTest.java281
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/wizards/newproject/StubProjectCreationPage.java11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java15
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java45
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java15
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java132
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilitiesTest.java119
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PointTestCases.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java120
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java60
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java35
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java69
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/QualifierListTest.java76
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/xml/HyperlinksTest.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java178
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java51
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ResourceNameValidatorTest.java39
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.traceview/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.traceview/META-INF/MANIFEST.MF4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.traceview/about.ini2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.traceview/about.properties7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.traceview/build.properties4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.traceview/icons/traceview-32.pngbin0 -> 2267 bytes
-rwxr-xr-xeclipse/scripts/create_all_symlinks.sh2
-rwxr-xr-xeclipse/scripts/create_ddms_symlinks.sh7
-rwxr-xr-xeclipse/scripts/create_hierarchyviewer_symlinks.sh7
-rwxr-xr-xeclipse/scripts/create_sdkman_symlinks.sh16
-rwxr-xr-x[-rw-r--r--]eclipse/scripts/update_version.sh12
-rw-r--r--eclipse/sites/external/site.xml8
-rw-r--r--eclipse/sites/internal/.gitignore2
-rw-r--r--eclipse/sites/internal/site.xml12
-rw-r--r--emulator/mksdcard/mksdcard.c71
-rw-r--r--emulator/qemud/qemud.c2
-rw-r--r--emulator/tests/Android.mk17
-rw-r--r--emulator/tests/test-qemud-pipes.c113
-rw-r--r--eventanalyzer/NOTICE190
-rw-r--r--files/devices.xml29
-rw-r--r--files/tools_source.properties2
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewManager.java4
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java58
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/ui/action/DumpDisplayListAction.java39
-rw-r--r--hierarchyviewer2/app/.classpath1
-rw-r--r--hierarchyviewer2/app/Android.mk27
-rw-r--r--hierarchyviewer2/app/NOTICE190
-rwxr-xr-xhierarchyviewer2/app/README69
-rwxr-xr-xhierarchyviewer2/app/etc/hierarchyviewer8
-rwxr-xr-xhierarchyviewer2/app/etc/hierarchyviewer.bat2
-rw-r--r--hierarchyviewer2/app/src/com/android/hierarchyviewer/AboutDialog.java4
-rw-r--r--hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java98
-rw-r--r--hierarchyviewer2/app/src/com/android/hierarchyviewer/actions/AboutAction.java2
-rw-r--r--hierarchyviewer2/app/src/com/android/hierarchyviewer/util/ActionButton.java4
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/NOTICE190
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk2
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java13
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/actions/DumpDisplayListAction.java56
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java14
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/DeviceSelectionModel.java41
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/auto-refresh.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/auto-refresh.png)bin541 -> 541 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/capture-psd.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/capture-psd.png)bin339 -> 339 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view-selected.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view-selected.png)bin254 -> 254 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view.png)bin228 -> 228 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/display.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/display.png)bin946 -> 946 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/filtered.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/filtered.png)bin9242 -> 9242 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/green.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png)bin302 -> 302 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/inspect-screenshot.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/inspect-screenshot.png)bin412 -> 412 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/invalidate.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/invalidate.png)bin391 -> 391 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-all-views.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-all-views.png)bin728 -> 728 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-overlay.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-overlay.png)bin549 -> 549 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-view-hierarchy.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-view-hierarchy.png)bin288 -> 288 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/not-selected.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/not-selected.png)bin12468 -> 12468 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-black.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-black.png)bin157 -> 157 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-white.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-white.png)bin158 -> 158 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view-selected.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view-selected.png)bin734 -> 734 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view.png)bin733 -> 733 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/red.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png)bin383 -> 383 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/refresh-windows.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/refresh-windows.png)bin872 -> 872 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/request-layout.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/request-layout.png)bin223 -> 223 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/save.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/save.png)bin360 -> 360 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-128.pngbin0 -> 17512 bytes
-rwxr-xr-xhierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-16.pngbin0 -> 880 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered-small.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered-small.png)bin5182 -> 5182 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered.png)bin9015 -> 9015 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-small.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-small.png)bin12611 -> 12611 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected.png)bin12159 -> 12159 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-extras.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-extras.png)bin330 -> 330 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-overlay.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-overlay.png)bin958 -> 958 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view-selected.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view-selected.png)bin276 -> 276 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view.png)bin281 -> 281 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/images/yellow.png (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png)bin255 -> 255 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about-small.jpgbin1578 -> 0 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about.jpgbin17237 -> 0 bytes
-rw-r--r--ide_common/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--ide_common/NOTICE190
-rw-r--r--ide_common/src/com/android/ide/common/rendering/LayoutLibrary.java53
-rw-r--r--ide_common/src/com/android/ide/common/resources/FrameworkResourceItem.java40
-rw-r--r--ide_common/src/com/android/ide/common/resources/FrameworkResources.java202
-rw-r--r--ide_common/src/com/android/ide/common/resources/InlineResourceItem.java71
-rw-r--r--ide_common/src/com/android/ide/common/resources/IntArrayWrapper.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IntArrayWrapper.java)6
-rw-r--r--ide_common/src/com/android/ide/common/resources/MultiResourceFile.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java)115
-rw-r--r--ide_common/src/com/android/ide/common/resources/ResourceDeltaKind.java26
-rw-r--r--ide_common/src/com/android/ide/common/resources/ResourceFile.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java)44
-rw-r--r--ide_common/src/com/android/ide/common/resources/ResourceFolder.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java)191
-rw-r--r--ide_common/src/com/android/ide/common/resources/ResourceItem.java237
-rw-r--r--ide_common/src/com/android/ide/common/resources/ResourceRepository.java546
-rw-r--r--ide_common/src/com/android/ide/common/resources/ResourceResolver.java109
-rw-r--r--ide_common/src/com/android/ide/common/resources/SingleResourceFile.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java)97
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/Configurable.java28
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/CountryCodeQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/CountryCodeQualifier.java)15
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/DockModeQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/DockModeQualifier.java)15
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/EnumBasedResourceQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/EnumBasedResourceQualifier.java)6
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/FolderConfiguration.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java)172
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/KeyboardStateQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/KeyboardStateQualifier.java)14
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/LanguageQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/LanguageQualifier.java)15
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/NavigationMethodQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationMethodQualifier.java)15
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/NavigationStateQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationStateQualifier.java)14
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/NetworkCodeQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NetworkCodeQualifier.java)15
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/NightModeQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NightModeQualifier.java)14
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/PixelDensityQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/PixelDensityQualifier.java)14
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/RegionQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/RegionQualifier.java)15
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/ResourceQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ResourceQualifier.java)12
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenDimensionQualifier.java)15
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenOrientationQualifier.java)14
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/ScreenRatioQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenRatioQualifier.java)14
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/ScreenSizeQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenSizeQualifier.java)14
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/TextInputMethodQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/TextInputMethodQualifier.java)14
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/TouchScreenQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/TouchScreenQualifier.java)14
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/VersionQualifier.java)15
-rw-r--r--ide_common/tests/.classpath9
-rw-r--r--ide_common/tests/.project17
-rw-r--r--ide_common/tests/Android.mk (renamed from draw9patch/src/Android.mk)15
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/CountryCodeQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/CountryCodeQualifierTest.java)9
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/DockModeQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/DockModeQualifierTest.java)7
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/FolderConfigurationTest.java38
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/KeyboardStateQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/KeyboardStateQualifierTest.java)8
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/LanguageQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/LanguageQualifierTest.java)17
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/NavigationMethodQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/NavigationMethodQualifierTest.java)8
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/NetworkCodeQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/NetworkCodeQualifierTest.java)9
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/PixelDensityQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/PixelDensityQualifierTest.java)8
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/RegionQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/RegionQualifierTest.java)9
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/ScreenDimensionQualifierTest.java)13
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/ScreenOrientationQualifierTest.java)8
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/ScreenSizeQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/ScreenSizeQualifierTest.java)8
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/TextInputMethodQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/TextInputMethodQualifierTest.java)8
-rw-r--r--ide_common/tests/src/com/android/ide/common/resources/configuration/TouchScreenQualifierTest.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/TouchScreenQualifierTest.java)8
-rw-r--r--layoutlib_api/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--layoutlib_api/NOTICE190
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/AdapterBinding.java81
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/Bridge.java38
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java31
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/DataBindingItem.java96
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/DensityBasedResourceValue.java32
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/ILayoutPullParser.java3
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/IProjectCallback.java67
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/RenderSession.java23
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/ResourceReference.java103
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/ResourceValue.java67
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/SessionParams.java146
-rw-r--r--layoutlib_api/src/com/android/ide/common/rendering/api/StyleResourceValue.java2
-rw-r--r--layoutopt/NOTICE190
-rwxr-xr-xmonkeyrunner/etc/monkeyrunner25
-rw-r--r--monkeyrunner/etc/monkeyrunner.bat19
-rw-r--r--monkeyrunner/src/Android.mk4
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java22
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java109
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/MonkeyImage.java179
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java27
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerStarter.java10
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/adb/AdbBackend.java24
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java23
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyImage.java4
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/adb/image/CaptureRawAndConvertedImage.java12
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyController.java8
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyControllerFrame.java10
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyBackend.java (renamed from monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerBackend.java)15
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyDevice.java200
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyImage.java36
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/core/MonkeyImageBase.java219
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/easy/By.java85
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/easy/EasyMonkeyDevice.java228
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/easy/HierarchyViewer.java158
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/easy/README27
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java7
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java16
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java4
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java4
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java3
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java3
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java7
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java7
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/stub/StubBackend.java14
-rw-r--r--monkeyrunner/test/com/android/monkeyrunner/JythonUtilsTest.java35
-rw-r--r--ninepatch/Android.mk3
-rw-r--r--ninepatch/NOTICE190
-rw-r--r--ninepatch/tests/.classpath8
-rw-r--r--ninepatch/tests/.project17
-rw-r--r--ninepatch/tests/Android.mk (renamed from hierarchyviewer2/app/src/Android.mk)22
-rw-r--r--ninepatch/tests/res/com/android/ninepatch/button.9.pngbin0 -> 3750 bytes
-rw-r--r--ninepatch/tests/src/com/android/ninepatch/NinePatchTest.java48
-rw-r--r--screenshot/NOTICE190
-rw-r--r--sdklauncher/NOTICE190
-rw-r--r--sdkmanager/app/.classpath26
-rw-r--r--sdkmanager/app/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--sdkmanager/app/Android.mk34
-rw-r--r--sdkmanager/app/NOTICE190
-rwxr-xr-xsdkmanager/app/etc/android13
-rwxr-xr-xsdkmanager/app/etc/android.bat4
-rw-r--r--sdkmanager/app/etc/manifest.txt2
-rw-r--r--sdkmanager/app/src/Android.mk26
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java38
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/Main.java183
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java263
-rwxr-xr-xsdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java2
-rw-r--r--sdkmanager/app/tests/Android.mk (renamed from androidprefs/src/Android.mk)14
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java77
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/MainTest.java174
-rwxr-xr-xsdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java182
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java88
-rw-r--r--sdkmanager/libs/sdklib/.classpath4
-rw-r--r--sdkmanager/libs/sdklib/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--sdkmanager/libs/sdklib/Android.mk26
-rw-r--r--sdkmanager/libs/sdklib/NOTICE190
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java35
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java12
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java42
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java44
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java77
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java334
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java734
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java38
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java6
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java5
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java8
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java6
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java22
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java17
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java10
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java51
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java14
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java28
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java53
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java34
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java6
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java56
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java11
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java30
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java15
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java18
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java33
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java2
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java25
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java7
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java6
-rw-r--r--sdkmanager/libs/sdklib/tests/Android.mk28
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/build/DebugKeyProviderTest.java125
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/AddonsListFetcherTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/AddonsListFetcherTest.java)4
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/BrokenPackageTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/BrokenPackageTest.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockAddonPackage.java)9
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockBrokenPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockBrokenPackage.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockEmptySdkManager.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockEmptySdkManager.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockMonitor.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockMonitor.java)34
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformPackage.java)9
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformToolPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformToolPackage.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockToolPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockToolPackage.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkAddonSourceTest.java)5
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java)24
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/mock/MockLog.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/mock/MockLog.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/SdkRepositoryTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/SdkRepositoryTest.java)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp.xml)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp2.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp2.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addon_sample_1.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addons_list_sample_1.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addons_list_sample_1.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_1.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_1.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_2.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_3.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_3.xml)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/AndroidManifestParserTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/AndroidManifestParserTest.java)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/SupportsScreensTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java)0
-rw-r--r--sdkmanager/libs/sdkuilib/.classpath5
-rw-r--r--sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.core.prefs64
-rwxr-xr-xsdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.ui.prefs54
-rw-r--r--sdkmanager/libs/sdkuilib/Android.mk45
-rw-r--r--sdkmanager/libs/sdkuilib/NOTICE190
-rw-r--r--sdkmanager/libs/sdkuilib/README44
-rw-r--r--sdkmanager/libs/sdkuilib/src/Android.mk21
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AddonSitesDialog.java417
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java3
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java30
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java3
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java1441
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java992
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java29
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java75
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java221
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java87
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java182
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java586
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java8
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.pngbin8713 -> 17715 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/collapsed_16.pngbin0 -> 329 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/expanded_16.pngbin0 -> 366 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon_16.png (renamed from sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png)bin397 -> 397 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_installed_16.pngbin0 -> 356 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_new_16.pngbin0 -> 299 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_update_16.pngbin0 -> 296 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_16.pngbin0 -> 388 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_other_16.pngbin0 -> 335 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/status_ok_16.pngbin0 -> 264 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_disabled_16.pngbin0 -> 321 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_enabled_16.pngbin0 -> 327 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/IProgressUiProvider.java93
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java213
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java (renamed from sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressDialog.java)75
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java258
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java41
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java259
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java340
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java9
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java36
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java2
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java10
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/IUpdaterWindow.java68
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java15
-rw-r--r--sdkmanager/libs/sdkuilib/tests/Android.mk (renamed from anttasks/src/Android.mk)21
-rwxr-xr-xsdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java18
-rwxr-xr-xsdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java3
-rw-r--r--sdkstats/NOTICE190
-rw-r--r--swtmenubar/.classpath7
-rw-r--r--swtmenubar/.gitignore1
-rw-r--r--swtmenubar/.project17
-rw-r--r--swtmenubar/Android.mk (renamed from sdkmanager/libs/sdklib/src/Android.mk)26
-rw-r--r--swtmenubar/MODULE_LICENSE_EPL0
-rw-r--r--swtmenubar/NOTICE224
-rwxr-xr-xswtmenubar/README80
-rwxr-xr-xswtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java134
-rw-r--r--swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java341
-rw-r--r--swtmenubar/src/com/android/menubar/IMenuBarCallback.java42
-rw-r--r--swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java73
-rw-r--r--swtmenubar/src/com/android/menubar/MenuBarEnhancer.java218
-rw-r--r--traceview/NOTICE190
-rw-r--r--traceview/src/com/android/traceview/MainWindow.java10
-rw-r--r--traceview/src/com/android/traceview/TimeLineView.java50
-rw-r--r--traceview/src/resources/icons/traceview-128.pngbin0 -> 17131 bytes
987 files changed, 46156 insertions, 8698 deletions
diff --git a/androidprefs/Android.mk b/androidprefs/Android.mk
index 363b085..08fd10d 100644
--- a/androidprefs/Android.mk
+++ b/androidprefs/Android.mk
@@ -13,5 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-JARUTILS_LOCAL_DIR := $(call my-dir)
-include $(JARUTILS_LOCAL_DIR)/src/Android.mk
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_MODULE := androidprefs
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/androidprefs/NOTICE b/androidprefs/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/androidprefs/NOTICE
@@ -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/anttasks/.classpath b/anttasks/.classpath
index 08ced21..9fc1c9c 100644
--- a/anttasks/.classpath
+++ b/anttasks/.classpath
@@ -4,5 +4,6 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/ant/ant.jar"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/common"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/anttasks/Android.mk b/anttasks/Android.mk
index 15ee903..e75a7cd 100644
--- a/anttasks/Android.mk
+++ b/anttasks/Android.mk
@@ -13,5 +13,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-ANTTASKS_LOCAL_DIR := $(call my-dir)
-include $(ANTTASKS_LOCAL_DIR)/src/Android.mk
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_JAR_MANIFEST := etc/manifest.txt
+
+LOCAL_JAVA_LIBRARIES := \
+ sdklib \
+ ant
+
+LOCAL_MODULE := anttasks
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/anttasks/NOTICE b/anttasks/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/anttasks/NOTICE
@@ -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/anttasks/etc/manifest.txt b/anttasks/etc/manifest.txt
index 0d89364..257f2aa 100644
--- a/anttasks/etc/manifest.txt
+++ b/anttasks/etc/manifest.txt
@@ -1 +1 @@
-Class-Path: sdklib.jar
+Class-Path: common.jar sdklib.jar
diff --git a/anttasks/src/com/android/ant/SetupTask.java b/anttasks/src/com/android/ant/SetupTask.java
index 6dc2c0f..e15f77b 100644
--- a/anttasks/src/com/android/ant/SetupTask.java
+++ b/anttasks/src/com/android/ant/SetupTask.java
@@ -16,6 +16,8 @@
package com.android.ant;
+import com.android.io.FileWrapper;
+import com.android.io.FolderWrapper;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
@@ -24,8 +26,6 @@ import com.android.sdklib.SdkManager;
import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
-import com.android.sdklib.io.FileWrapper;
-import com.android.sdklib.io.FolderWrapper;
import com.android.sdklib.xml.AndroidManifest;
import com.android.sdklib.xml.AndroidXPathFactory;
diff --git a/apkbuilder/NOTICE b/apkbuilder/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/apkbuilder/NOTICE
@@ -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/archquery/Android.mk b/archquery/Android.mk
index 53cad46..8936178 100644
--- a/archquery/Android.mk
+++ b/archquery/Android.mk
@@ -13,5 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-ARCHQUERY_LOCAL_DIR := $(call my-dir)
-include $(ARCHQUERY_LOCAL_DIR)/src/Android.mk
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_JAR_MANIFEST := etc/manifest.txt
+
+LOCAL_JAVA_LIBRARIES := \
+
+LOCAL_MODULE := archquery
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/archquery/NOTICE b/archquery/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/archquery/NOTICE
@@ -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/build/patch_windows_sdk.sh b/build/patch_windows_sdk.sh
index 2bbf24c..b01e41e 100755
--- a/build/patch_windows_sdk.sh
+++ b/build/patch_windows_sdk.sh
@@ -22,7 +22,8 @@ TOPDIR=${TOPDIR:-$3}
# Remove obsolete stuff from tools
TOOLS=$TEMP_SDK_DIR/tools
LIB=$TEMP_SDK_DIR/tools/lib
-rm $V $TOOLS/{android,apkbuilder,ddms,draw9patch,emulator}
+rm $V $TOOLS/{android,apkbuilder,ddms,draw9patch}
+rm $V $TOOLS/{emulator,emulator-arm,emulator-x86}
rm $V $TOOLS/{hierarchyviewer,layoutopt,mksdcard,traceview,monkeyrunner}
rm $V $TOOLS/proguard/bin/*.sh
diff --git a/build/tools.atree b/build/tools.atree
index d40d8e3..2e566e6 100644
--- a/build/tools.atree
+++ b/build/tools.atree
@@ -40,8 +40,12 @@ bin/mksdcard tools/mksdcard
bin/zipalign tools/zipalign
# emulator
-bin/emulator tools/emulator
-sdk/emulator/snapshot/snapshots.img tools/lib/emulator/snapshots.img
+bin/emulator tools/emulator
+bin/emulator-x86 tools/emulator-x86
+bin/emulator-arm tools/emulator-arm
+sdk/emulator/snapshot/snapshots.img tools/lib/emulator/snapshots.img
+usr/share/pc-bios/bios.bin tools/lib/pc-bios/bios.bin
+usr/share/pc-bios/vgabios-cirrus.bin tools/lib/pc-bios/vgabios-cirrus.bin
# Java-Based SDK Tools
bin/ddms tools/ddms
@@ -74,6 +78,7 @@ sdk/files/android.el tools/lib/android.el
# Java Libraries for the tools
framework/androidprefs.jar tools/lib/androidprefs.jar
framework/common.jar tools/lib/common.jar
+framework/swtmenubar.jar tools/lib/swtmenubar.jar
sdk/apkbuilder/etc/apkbuilder tools/apkbuilder
framework/sdkstats.jar tools/lib/sdkstats.jar
framework/archquery.jar tools/lib/archquery.jar
@@ -127,3 +132,8 @@ external/proguard/src/proguard/ant/task.properties tools/proguard/ant
##############################################################################
sdk/testapps tests/testapps
+framework/ddmlib-tests.jar tests/libtests/ddmlib-tests.jar
+framework/ninepatch-tests.jar tests/libtests/ninepatch-tests.jar
+framework/common-tests.jar tests/libtests/common-tests.jar
+framework/sdklib-tests.jar tests/libtests/sdklib-tests.jar
+framework/sdkuilib-tests.jar tests/libtests/sdkuilib-tests.jar
diff --git a/build/windows_sdk_tools.mk b/build/windows_sdk_tools.mk
index 8164cc5..73f4d12 100644
--- a/build/windows_sdk_tools.mk
+++ b/build/windows_sdk_tools.mk
@@ -5,6 +5,8 @@
WIN_SDK_TARGETS := \
emulator \
+ emulator-arm \
+ emulator-x86 \
mksdcard \
sdklauncher
diff --git a/changes.txt b/changes.txt
index 970ba93..73bd929 100644
--- a/changes.txt
+++ b/changes.txt
@@ -1,5 +1,13 @@
Change log for Android SDK Tools.
+Revision 11:
+- See eclipse/changes.txt for ADT related changes.
+
+Revision 10:
+- The tools now automatically generate Java Programming Language
+ source files (in the gen directory) and bytecode (in the res/raw
+ directory) from your native .rs files
+
Revision 9:
- Fix packaging issue that broke draw9patch
- Ant build rules will now check the Ant version and fail if it's older than 1.8
diff --git a/common/Android.mk b/common/Android.mk
index aa2ba8e..ef907f2 100644
--- a/common/Android.mk
+++ b/common/Android.mk
@@ -25,3 +25,6 @@ LOCAL_MODULE := common
LOCAL_MODULE_TAGS := optional
include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/common/NOTICE b/common/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/common/NOTICE
@@ -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/common/src/com/android/AndroidConstants.java b/common/src/com/android/AndroidConstants.java
new file mode 100644
index 0000000..dfe137a
--- /dev/null
+++ b/common/src/com/android/AndroidConstants.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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;
+
+/**
+ * Generic Android Constants.
+ */
+public class AndroidConstants {
+
+ /** Default anim resource folder name, i.e. "anim" */
+ public final static String FD_RES_ANIM = "anim"; //$NON-NLS-1$
+ /** Default animator resource folder name, i.e. "animator" */
+ public final static String FD_RES_ANIMATOR = "animator"; //$NON-NLS-1$
+ /** Default color resource folder name, i.e. "color" */
+ public final static String FD_RES_COLOR = "color"; //$NON-NLS-1$
+ /** Default drawable resource folder name, i.e. "drawable" */
+ public final static String FD_RES_DRAWABLE = "drawable"; //$NON-NLS-1$
+ /** Default interpolator resource folder name, i.e. "interpolator" */
+ public final static String FD_RES_INTERPOLATOR = "interpolator"; //$NON-NLS-1$
+ /** Default layout resource folder name, i.e. "layout" */
+ public final static String FD_RES_LAYOUT = "layout"; //$NON-NLS-1$
+ /** Default menu resource folder name, i.e. "menu" */
+ public final static String FD_RES_MENU = "menu"; //$NON-NLS-1$
+ /** Default menu resource folder name, i.e. "mipmap" */
+ public final static String FD_RES_MIPMAP = "mipmap"; //$NON-NLS-1$
+ /** Default values resource folder name, i.e. "values" */
+ public final static String FD_RES_VALUES = "values"; //$NON-NLS-1$
+ /** Default xml resource folder name, i.e. "xml" */
+ public final static String FD_RES_XML = "xml"; //$NON-NLS-1$
+ /** Default raw resource folder name, i.e. "raw" */
+ public final static String FD_RES_RAW = "raw"; //$NON-NLS-1$
+
+ /** Separator between the resource folder qualifier. */
+ public final static String RES_QUALIFIER_SEP = "-"; //$NON-NLS-1$
+
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java b/common/src/com/android/io/FileWrapper.java
index afc19b2..2859c0d 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java
+++ b/common/src/com/android/io/FileWrapper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.sdklib.io;
+package com.android.io;
import java.io.File;
@@ -137,6 +137,10 @@ public class FileWrapper extends File implements IAbstractFile {
return isFile();
}
+ public long getModificationStamp() {
+ return lastModified();
+ }
+
public IAbstractFolder getParentFolder() {
String p = this.getParent();
if (p == null) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java b/common/src/com/android/io/FolderWrapper.java
index 33e31c1..26ed9cf 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java
+++ b/common/src/com/android/io/FolderWrapper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.sdklib.io;
+package com.android.io;
import java.io.File;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java b/common/src/com/android/io/IAbstractFile.java
index 2ff1fc8..6dfc8d8 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java
+++ b/common/src/com/android/io/IAbstractFile.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.sdklib.io;
+package com.android.io;
import java.io.InputStream;
import java.io.OutputStream;
@@ -50,4 +50,9 @@ public interface IAbstractFile extends IAbstractResource {
* Returns the preferred mode to write into the file.
*/
PreferredWriteMode getPreferredWriteMode();
+
+ /**
+ * Returns the last modification timestamp
+ */
+ long getModificationStamp();
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java b/common/src/com/android/io/IAbstractFolder.java
index 17c0dbd..8335ef9 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java
+++ b/common/src/com/android/io/IAbstractFolder.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.sdklib.io;
+package com.android.io;
import java.io.File;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java b/common/src/com/android/io/IAbstractResource.java
index 0ccb107..3d762eb 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java
+++ b/common/src/com/android/io/IAbstractResource.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.sdklib.io;
+package com.android.io;
/**
* Base representation of a file system resource.<p/>
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/StreamException.java b/common/src/com/android/io/StreamException.java
index 2088864..f67c7a8 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/StreamException.java
+++ b/common/src/com/android/io/StreamException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.sdklib.io;
+package com.android.io;
/**
* Exception thrown when {@link IAbstractFile#getContents()} fails.
diff --git a/common/src/com/android/resources/FolderTypeRelationship.java b/common/src/com/android/resources/FolderTypeRelationship.java
new file mode 100644
index 0000000..34961a3
--- /dev/null
+++ b/common/src/com/android/resources/FolderTypeRelationship.java
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+package com.android.resources;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class gives access to the bidirectional relationship between {@link ResourceType} and
+ * {@link ResourceFolderType}.
+ */
+public final class FolderTypeRelationship {
+
+ private final static Map<ResourceType, List<ResourceFolderType>> mTypeToFolderMap =
+ new HashMap<ResourceType, List<ResourceFolderType>>();
+
+ private final static Map<ResourceFolderType, List<ResourceType>> mFolderToTypeMap =
+ new HashMap<ResourceFolderType, List<ResourceType>>();
+
+ static {
+ // generate the relationships in a temporary map
+ add(ResourceType.ANIM, ResourceFolderType.ANIM);
+ add(ResourceType.ANIMATOR, ResourceFolderType.ANIMATOR);
+ add(ResourceType.ARRAY, ResourceFolderType.VALUES);
+ add(ResourceType.ATTR, ResourceFolderType.VALUES);
+ add(ResourceType.BOOL, ResourceFolderType.VALUES);
+ add(ResourceType.COLOR, ResourceFolderType.VALUES);
+ add(ResourceType.COLOR, ResourceFolderType.COLOR);
+ add(ResourceType.DECLARE_STYLEABLE, ResourceFolderType.VALUES);
+ add(ResourceType.DIMEN, ResourceFolderType.VALUES);
+ add(ResourceType.DRAWABLE, ResourceFolderType.VALUES);
+ add(ResourceType.DRAWABLE, ResourceFolderType.DRAWABLE);
+ add(ResourceType.FRACTION, ResourceFolderType.VALUES);
+ add(ResourceType.ID, ResourceFolderType.VALUES);
+ add(ResourceType.INTEGER, ResourceFolderType.VALUES);
+ add(ResourceType.INTERPOLATOR, ResourceFolderType.INTERPOLATOR);
+ add(ResourceType.LAYOUT, ResourceFolderType.LAYOUT);
+ add(ResourceType.MENU, ResourceFolderType.MENU);
+ add(ResourceType.MIPMAP, ResourceFolderType.MIPMAP);
+ add(ResourceType.PLURALS, ResourceFolderType.VALUES);
+ add(ResourceType.PUBLIC, ResourceFolderType.VALUES);
+ add(ResourceType.RAW, ResourceFolderType.RAW);
+ add(ResourceType.STRING, ResourceFolderType.VALUES);
+ add(ResourceType.STYLE, ResourceFolderType.VALUES);
+ add(ResourceType.STYLEABLE, ResourceFolderType.VALUES);
+ add(ResourceType.XML, ResourceFolderType.XML);
+
+ makeSafe();
+ }
+
+ /**
+ * Returns a list of {@link ResourceType}s that can be generated from files inside a folder
+ * of the specified type.
+ * @param folderType The folder type.
+ * @return a list of {@link ResourceType}, possibly empty but never null.
+ */
+ public static List<ResourceType> getRelatedResourceTypes(ResourceFolderType folderType) {
+ List<ResourceType> list = mFolderToTypeMap.get(folderType);
+ if (list != null) {
+ return list;
+ }
+
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns a list of {@link ResourceFolderType} that can contain files generating resources
+ * of the specified type.
+ * @param resType the type of resource.
+ * @return a list of {@link ResourceFolderType}, possibly empty but never null.
+ */
+ public static List<ResourceFolderType> getRelatedFolders(ResourceType resType) {
+ List<ResourceFolderType> list = mTypeToFolderMap.get(resType);
+ if (list != null) {
+ return list;
+ }
+
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns true if the {@link ResourceType} and the {@link ResourceFolderType} values match.
+ * @param resType the resource type.
+ * @param folderType the folder type.
+ * @return true if files inside the folder of the specified {@link ResourceFolderType}
+ * could generate a resource of the specified {@link ResourceType}
+ */
+ public static boolean match(ResourceType resType, ResourceFolderType folderType) {
+ List<ResourceFolderType> list = mTypeToFolderMap.get(resType);
+
+ if (list != null) {
+ return list.contains(folderType);
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds a {@link ResourceType} - {@link ResourceFolderType} relationship. this indicates that
+ * a file in the folder can generate a resource of the specified type.
+ * @param type The resourceType
+ * @param folder The {@link ResourceFolderType}
+ */
+ private static void add(ResourceType type, ResourceFolderType folder) {
+ // first we add the folder to the list associated with the type.
+ List<ResourceFolderType> folderList = mTypeToFolderMap.get(type);
+ if (folderList == null) {
+ folderList = new ArrayList<ResourceFolderType>();
+ mTypeToFolderMap.put(type, folderList);
+ }
+ if (folderList.indexOf(folder) == -1) {
+ folderList.add(folder);
+ }
+
+ // now we add the type to the list associated with the folder.
+ List<ResourceType> typeList = mFolderToTypeMap.get(folder);
+ if (typeList == null) {
+ typeList = new ArrayList<ResourceType>();
+ mFolderToTypeMap.put(folder, typeList);
+ }
+ if (typeList.indexOf(type) == -1) {
+ typeList.add(type);
+ }
+ }
+
+ /**
+ * Makes the maps safe by replacing the current list values with unmodifiable lists.
+ */
+ private static void makeSafe() {
+ for (ResourceType type : ResourceType.values()) {
+ List<ResourceFolderType> list = mTypeToFolderMap.get(type);
+ if (list != null) {
+ // replace with a unmodifiable list wrapper around the current list.
+ mTypeToFolderMap.put(type, Collections.unmodifiableList(list));
+ }
+ }
+
+ for (ResourceFolderType folder : ResourceFolderType.values()) {
+ List<ResourceType> list = mFolderToTypeMap.get(folder);
+ if (list != null) {
+ // replace with a unmodifiable list wrapper around the current list.
+ mFolderToTypeMap.put(folder, Collections.unmodifiableList(list));
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolderType.java b/common/src/com/android/resources/ResourceFolderType.java
index 91aec28..3a5b64d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolderType.java
+++ b/common/src/com/android/resources/ResourceFolderType.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,26 +14,25 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.manager;
+package com.android.resources;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.sdklib.SdkConstants;
+import com.android.AndroidConstants;
/**
* Enum representing a type of resource folder.
*/
public enum ResourceFolderType {
- ANIM(SdkConstants.FD_ANIM),
- ANIMATOR(SdkConstants.FD_ANIMATOR),
- COLOR(SdkConstants.FD_COLOR),
- DRAWABLE(SdkConstants.FD_DRAWABLE),
- INTERPOLATOR(SdkConstants.FD_INTERPOLATOR),
- LAYOUT(SdkConstants.FD_LAYOUT),
- MENU(SdkConstants.FD_MENU),
- MIPMAP(SdkConstants.FD_MIPMAP),
- RAW(SdkConstants.FD_RAW),
- VALUES(SdkConstants.FD_VALUES),
- XML(SdkConstants.FD_XML);
+ ANIM(AndroidConstants.FD_RES_ANIM),
+ ANIMATOR(AndroidConstants.FD_RES_ANIMATOR),
+ COLOR(AndroidConstants.FD_RES_COLOR),
+ DRAWABLE(AndroidConstants.FD_RES_DRAWABLE),
+ INTERPOLATOR(AndroidConstants.FD_RES_INTERPOLATOR),
+ LAYOUT(AndroidConstants.FD_RES_LAYOUT),
+ MENU(AndroidConstants.FD_RES_MENU),
+ MIPMAP(AndroidConstants.FD_RES_MIPMAP),
+ RAW(AndroidConstants.FD_RES_RAW),
+ VALUES(AndroidConstants.FD_RES_VALUES),
+ XML(AndroidConstants.FD_RES_XML);
private final String mName;
@@ -71,7 +70,7 @@ public enum ResourceFolderType {
*/
public static ResourceFolderType getFolderType(String folderName) {
// split the name of the folder in segments.
- String[] folderSegments = folderName.split(FolderConfiguration.QUALIFIER_SEP);
+ String[] folderSegments = folderName.split(AndroidConstants.RES_QUALIFIER_SEP);
// get the enum for the resource type.
return getTypeByName(folderSegments[0]);
diff --git a/common/src/com/android/resources/ResourceType.java b/common/src/com/android/resources/ResourceType.java
index a4d3aa2..e9d4d53 100644
--- a/common/src/com/android/resources/ResourceType.java
+++ b/common/src/com/android/resources/ResourceType.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
diff --git a/common/tests/.classpath b/common/tests/.classpath
new file mode 100644
index 0000000..b793adc
--- /dev/null
+++ b/common/tests/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/common"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/common/tests/.project b/common/tests/.project
new file mode 100644
index 0000000..9f550a3
--- /dev/null
+++ b/common/tests/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>common-tests</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/archquery/src/Android.mk b/common/tests/Android.mk
index 980f002..10e75e8 100644
--- a/archquery/src/Android.mk
+++ b/common/tests/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2009 The Android Open Source Project
+# Copyright (C) 2011 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.
@@ -12,14 +11,17 @@
# 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 := \
+# Only compile source java files in this lib.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := common-tests
+LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := archquery
+LOCAL_JAVA_LIBRARIES := common junit
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/manager/FolderTypeRelationShipTest.java b/common/tests/src/com/android/resources/FolderTypeRelationShipTest.java
index f1ce9d9..809eae7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/manager/FolderTypeRelationShipTest.java
+++ b/common/tests/src/com/android/resources/FolderTypeRelationShipTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.manager;
+package com.android.resources;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import junit.framework.TestCase;
@@ -27,7 +29,7 @@ public class FolderTypeRelationShipTest extends TestCase {
// loop on all the enum, and make sure there's at least one folder type for it.
for (ResourceType type : ResourceType.values()) {
assertTrue(type.getDisplayName(),
- FolderTypeRelationship.getRelatedFolders(type).length > 0);
+ FolderTypeRelationship.getRelatedFolders(type).size() > 0);
}
}
@@ -36,8 +38,7 @@ public class FolderTypeRelationShipTest extends TestCase {
// loop on all the enum, and make sure there's at least one res type for it.
for (ResourceFolderType type : ResourceFolderType.values()) {
assertTrue(type.getName(),
- FolderTypeRelationship.getRelatedResourceTypes(type).length > 0);
+ FolderTypeRelationship.getRelatedResourceTypes(type).size() > 0);
}
}
-
}
diff --git a/ddms/app/.classpath b/ddms/app/.classpath
index 1040688..6caf176 100644
--- a/ddms/app/.classpath
+++ b/ddms/app/.classpath
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry excluding="Makefile|resources/" kind="src" path="src"/>
- <classpathentry kind="src" path="src/resources"/>
+ <classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_OSGI"/>
<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
<classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
<classpathentry combineaccessrules="false" kind="src" path="/SdkStatsService"/>
+ <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/ddms/app/.settings/org.eclipse.jdt.core.prefs b/ddms/app/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/ddms/app/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/ddms/app/Android.mk b/ddms/app/Android.mk
index 3857706..d1e4a52 100644
--- a/ddms/app/Android.mk
+++ b/ddms/app/Android.mk
@@ -1,5 +1,30 @@
# Copyright 2007 The Android Open Source Project
#
-DDMSAPP_LOCAL_DIR := $(call my-dir)
-include $(DDMSAPP_LOCAL_DIR)/etc/Android.mk
-include $(DDMSAPP_LOCAL_DIR)/src/Android.mk
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_JAR_MANIFEST := etc/manifest.txt
+
+# If the dependency list is changed, etc/manifest.txt
+# MUST be updated as well (Except for swt.jar which is dynamically
+# added based on whether the VM is 32 or 64 bit)
+LOCAL_JAVA_LIBRARIES := \
+ androidprefs \
+ sdkstats \
+ ddmlib \
+ ddmuilib \
+ swt \
+ swtmenubar \
+ org.eclipse.jface_3.4.2.M20090107-0800 \
+ org.eclipse.equinox.common_3.4.0.v20080421-2006 \
+ org.eclipse.core.commands_3.4.0.I20080509-2000
+LOCAL_MODULE := ddms
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/ddms/app/NOTICE b/ddms/app/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/ddms/app/NOTICE
@@ -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/ddms/app/README b/ddms/app/README
index cc55ddd..42efb7b 100644
--- a/ddms/app/README
+++ b/ddms/app/README
@@ -1,11 +1,75 @@
-Using the Eclipse projects for ddms.
+Using the Eclipse project DDMS
+------------------------------
-ddms requires SWT to compile.
+DDMS requires some external libraries to compile.
+If you build DDMS using the makefile, you have nothing to configure.
+However if you want to develop on DDMS using Eclipse, you need to
+perform the following configuration.
-SWT is available in the depot under //device/prebuild/<platform>/swt
-Because the build path cannot contain relative path that are not inside the project directory,
-the .classpath file references a user library called ANDROID_SWT.
+-------
+1- Projects required in Eclipse
+-------
-In order to compile the project, make a user library called ANDROID_SWT containing the jar
-available at //device/prebuild/<platform>/swt. \ No newline at end of file
+To run DDMS from Eclipse, you need to import the following 5 projects:
+
+ - sdk/androidpprefs: project AndroidPrefs
+ - sdk/sdkstats: project SdkStatsService
+ - sdk/ddms/app: project Ddms
+ - sdk/ddms/libs/ddmlib: project Ddmlib
+ - sdk/ddms/libs/ddmuilib: project Ddmuilib
+
+
+-------
+2- DDMS requires some SWT and OSGI JARs to compile.
+-------
+
+SWT is available in the tree under prebuild/<platform>/swt
+
+Because the build path cannot contain relative path that are not inside
+the project directory, the .classpath file references a user library
+called ANDROID_SWT.
+SWT depends on OSGI, so we'll also create an ANDROID_OSGI library for that.
+
+In order to compile the project:
+- Open Preferences > Java > Build Path > User Libraries
+
+- Create a new user library named ANDROID_SWT
+- Add the following 4 JAR files:
+
+ - prebuilt/<platform>/swt/swt.jar
+ - prebuilt/common/eclipse/org.eclipse.core.commands_3.*.jar
+ - prebuilt/common/eclipse/org.eclipse.equinox.common_3.*.jar
+ - prebuilt/common/eclipse/org.eclipse.jface_3.*.jar
+
+- Create a new user library named ANDROID_OSGI
+- Add the following JAR file:
+
+ - prebuilt/common/osgi/osgi.jar
+
+
+-------
+3- DDMS also requires the compiled SwtMenuBar library.
+-------
+
+Build the swtmenubar library:
+$ cd $TOP (top of Android tree)
+$ . build/envsetup.sh && lunch sdk-eng
+$ sdk/eclipse/scripts/create_sdkman_symlinks.sh
+
+Define a classpath variable in Eclipse:
+- Open Preferences > Java > Build Path > Classpath Variables
+- Create a new classpath variable named ANDROID_OUT_FRAMEWORK
+- Set its folder value to <Android tree>/out/host/<platform>/framework
+- Create a new classpath variable named ANDROID_SRC
+- Set its folder value to <Android tree>
+
+You might need to clean the ddms project (Project > Clean...) after
+you add the new classpath variable, otherwise previous errors might not
+go away automatically.
+
+The ANDROID_SRC part should be optional. It allows you to have access to
+the SwtMenuBar generic parts from the Java editor.
+
+--
+EOF
diff --git a/ddms/app/etc/ddms b/ddms/app/etc/ddms
index 10b3a56..b0e529b 100755
--- a/ddms/app/etc/ddms
+++ b/ddms/app/etc/ddms
@@ -75,7 +75,7 @@ if [ `uname` = "Linux" ]; then
export GDK_NATIVE_WINDOWS=true
fi
-jarpath="$frameworkdir/$jarfile"
+jarpath="$frameworkdir/$jarfile:$frameworkdir/swtmenubar.jar"
# Figure out the path to the swt.jar for the current architecture.
# if ANDROID_SWT is defined, then just use this.
@@ -100,6 +100,8 @@ if [ ! -d "$swtpath" ]; then
exit 1
fi
-# need to use "java.ext.dirs" because "-jar" causes classpath to be ignored
-# might need more memory, e.g. -Xmx128M
-exec "$javaCmd" -Xmx256M $os_opts $java_debug -Dcom.android.ddms.bindir="$progdir" -classpath "$jarpath:$swtpath/swt.jar" com.android.ddms.Main "$@"
+exec "$javaCmd" \
+ -Xmx256M $os_opts $java_debug \
+ -Dcom.android.ddms.bindir="$progdir" \
+ -classpath "$jarpath:$swtpath/swt.jar" \
+ com.android.ddms.Main "$@"
diff --git a/ddms/app/etc/ddms.bat b/ddms/app/etc/ddms.bat
index 0b02cb3..4b0675d 100755
--- a/ddms/app/etc/ddms.bat
+++ b/ddms/app/etc/ddms.bat
@@ -48,7 +48,7 @@ if debug NEQ "%1" goto NoDebug
shift 1
:NoDebug
-set jarpath=%frameworkdir%%jarfile%
+set jarpath=%frameworkdir%%jarfile%;%frameworkdir%swtmenubar.jar
if not defined ANDROID_SWT goto QueryArch
set swt_path=%ANDROID_SWT%
diff --git a/ddms/app/etc/manifest.txt b/ddms/app/etc/manifest.txt
index e30c193..8c6ab23 100644
--- a/ddms/app/etc/manifest.txt
+++ b/ddms/app/etc/manifest.txt
@@ -1,3 +1,3 @@
Main-Class: com.android.ddms.Main
-Class-Path: androidprefs.jar sdkstats.jar ddmlib.jar ddmuilib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar
+Class-Path: androidprefs.jar sdkstats.jar ddmlib.jar ddmuilib.jar swtmenubar.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar
diff --git a/ddms/app/src/Android.mk b/ddms/app/src/Android.mk
deleted file mode 100644
index bac4030..0000000
--- a/ddms/app/src/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright 2007 The Android Open Source Project
-#
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_JAVA_RESOURCE_DIRS := resources
-
-LOCAL_JAR_MANIFEST := ../etc/manifest.txt
-
-# If the dependency list is changed, etc/manifest.txt
-# MUST be updated as well (Except for swt.jar which is dynamically
-# added based on whether the VM is 32 or 64 bit)
-LOCAL_JAVA_LIBRARIES := \
- androidprefs \
- sdkstats \
- ddmlib \
- ddmuilib \
- swt \
- org.eclipse.jface_3.4.2.M20090107-0800 \
- org.eclipse.equinox.common_3.4.0.v20080421-2006 \
- org.eclipse.core.commands_3.4.0.I20080509-2000
-LOCAL_MODULE := ddms
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
diff --git a/ddms/app/src/com/android/ddms/AboutDialog.java b/ddms/app/src/com/android/ddms/AboutDialog.java
index d9387c9..1d6bd89 100644
--- a/ddms/app/src/com/android/ddms/AboutDialog.java
+++ b/ddms/app/src/com/android/ddms/AboutDialog.java
@@ -64,7 +64,7 @@ public class AboutDialog extends Dialog {
Shell shell = new Shell(parent, getStyle());
shell.setText("About...");
- logoImage = loadImage(shell, "ddms-logo.png"); //$NON-NLS-1$
+ logoImage = loadImage(shell, "ddms-128.png"); //$NON-NLS-1$
createContents(shell);
shell.pack();
diff --git a/ddms/app/src/com/android/ddms/PrefsDialog.java b/ddms/app/src/com/android/ddms/PrefsDialog.java
index 418b8ba..c957a89 100644
--- a/ddms/app/src/com/android/ddms/PrefsDialog.java
+++ b/ddms/app/src/com/android/ddms/PrefsDialog.java
@@ -294,7 +294,11 @@ public final class PrefsDialog {
dlg.setPreferenceStore(mPrefStore);
// run it
- dlg.open();
+ try {
+ dlg.open();
+ } catch (Throwable t) {
+ Log.e("ddms", t);
+ }
// save prefs
try {
diff --git a/ddms/app/src/com/android/ddms/UIThread.java b/ddms/app/src/com/android/ddms/UIThread.java
index 2dc1cf0..cb6786a 100644
--- a/ddms/app/src/com/android/ddms/UIThread.java
+++ b/ddms/app/src/com/android/ddms/UIThread.java
@@ -50,6 +50,10 @@ import com.android.ddmuilib.logcat.LogColors;
import com.android.ddmuilib.logcat.LogFilter;
import com.android.ddmuilib.logcat.LogPanel;
import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
+import com.android.menubar.IMenuBarCallback;
+import com.android.menubar.IMenuBarEnhancer;
+import com.android.menubar.IMenuBarEnhancer.MenuBarMode;
+import com.android.menubar.MenuBarEnhancer;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
@@ -84,7 +88,6 @@ import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
-import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
@@ -101,6 +104,8 @@ import java.util.ArrayList;
* when {@link IDevice} / {@link Client} selection changes.
*/
public class UIThread implements IUiSelectionListener, IClientChangeListener {
+ private static final String APP_NAME = "DDMS";
+
/*
* UI tab panel definitions. The constants here must match up with the array
* indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
@@ -414,10 +419,10 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
/**
* Create SWT objects and drive the user interface event loop.
- * @param location location of the folder that contains ddms.
+ * @param ddmsParentLocation location of the folder that contains ddms.
*/
public void runUI(String ddmsParentLocation) {
- Display.setAppName("ddms");
+ Display.setAppName(APP_NAME);
mDisplay = new Display();
final Shell shell = new Shell(mDisplay);
@@ -425,7 +430,7 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
mDdmUiLibLoader = ImageLoader.getDdmUiLibLoader();
shell.setImage(ImageLoader.getLoader(this.getClass()).loadImage(mDisplay,
- "ddms-icon.png", //$NON-NLS-1$
+ "ddms-128.png", //$NON-NLS-1$
100, 50, null));
Log.setLogOutput(new ILogOutput() {
@@ -435,11 +440,11 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
// dialog box only run in UI thread..
mDisplay.asyncExec(new Runnable() {
public void run() {
- Shell shell = mDisplay.getActiveShell();
+ Shell activeShell = mDisplay.getActiveShell();
if (logLevel == LogLevel.ERROR) {
- MessageDialog.openError(shell, tag, message);
+ MessageDialog.openError(activeShell, tag, message);
} else {
- MessageDialog.openWarning(shell, tag, message);
+ MessageDialog.openWarning(activeShell, tag, message);
}
}
});
@@ -556,20 +561,20 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
shell.addControlListener(new ControlListener() {
public void controlMoved(ControlEvent e) {
// get the new x/y
- Rectangle rect = shell.getBounds();
+ Rectangle controlBounds = shell.getBounds();
// store in pref file
- PreferenceStore prefs = PrefsDialog.getStore();
- prefs.setValue(PrefsDialog.SHELL_X, rect.x);
- prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
+ PreferenceStore currentPrefs = PrefsDialog.getStore();
+ currentPrefs.setValue(PrefsDialog.SHELL_X, controlBounds.x);
+ currentPrefs.setValue(PrefsDialog.SHELL_Y, controlBounds.y);
}
public void controlResized(ControlEvent e) {
// get the new w/h
- Rectangle rect = shell.getBounds();
+ Rectangle controlBounds = shell.getBounds();
// store in pref file
- PreferenceStore prefs = PrefsDialog.getStore();
- prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
- prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
+ PreferenceStore currentPrefs = PrefsDialog.getStore();
+ currentPrefs.setValue(PrefsDialog.SHELL_WIDTH, controlBounds.width);
+ currentPrefs.setValue(PrefsDialog.SHELL_HEIGHT, controlBounds.height);
}
});
}
@@ -624,41 +629,31 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
shell.addControlListener(new ControlListener() {
public void controlMoved(ControlEvent e) {
// get the new x/y
- Rectangle rect = shell.getBounds();
+ Rectangle controlBounds = shell.getBounds();
// store in pref file
- PreferenceStore prefs = PrefsDialog.getStore();
- prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
- prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
+ PreferenceStore currentPrefs = PrefsDialog.getStore();
+ currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_X, controlBounds.x);
+ currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, controlBounds.y);
}
public void controlResized(ControlEvent e) {
// get the new w/h
- Rectangle rect = shell.getBounds();
+ Rectangle controlBounds = shell.getBounds();
// store in pref file
- PreferenceStore prefs = PrefsDialog.getStore();
- prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
- prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
+ PreferenceStore currentPrefs = PrefsDialog.getStore();
+ currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, controlBounds.width);
+ currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, controlBounds.height);
}
});
}
/*
- * Set the confirm-before-close dialog. TODO: enable/disable in prefs. TODO:
- * is there any point in having this?
+ * Set the confirm-before-close dialog.
*/
private void setConfirmClose(final Shell shell) {
- if (true)
- return;
-
- shell.addListener(SWT.Close, new Listener() {
- public void handleEvent(Event event) {
- int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO;
- MessageBox msgBox = new MessageBox(shell, style);
- msgBox.setText("Confirm...");
- msgBox.setMessage("Close DDM?");
- event.doit = (msgBox.open() == SWT.YES);
- }
- });
+ // Note: there was some commented out code to display a confirmation box
+ // when closing. The feature seems unnecessary and the code was not being
+ // used, so it has been removed.
}
/*
@@ -677,8 +672,6 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
actionItem.setText("&Actions");
MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE);
deviceItem.setText("&Device");
- MenuItem helpItem = new MenuItem(menuBar, SWT.CASCADE);
- helpItem.setText("&Help");
// create top-level menus
Menu fileMenu = new Menu(menuBar);
@@ -689,22 +682,11 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
actionItem.setMenu(actionMenu);
Menu deviceMenu = new Menu(menuBar);
deviceItem.setMenu(deviceMenu);
- Menu helpMenu = new Menu(menuBar);
- helpItem.setMenu(helpMenu);
MenuItem item;
// create File menu items
item = new MenuItem(fileMenu, SWT.NONE);
- item.setText("&Preferences...");
- item.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- PrefsDialog.run(shell);
- }
- });
-
- item = new MenuItem(fileMenu, SWT.NONE);
item.setText("&Static Port Configuration...");
item.addSelectionListener(new SelectionAdapter() {
@Override
@@ -714,18 +696,36 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
}
});
- new MenuItem(fileMenu, SWT.SEPARATOR);
+ IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenu(APP_NAME, fileMenu,
+ new IMenuBarCallback() {
+ public void printError(String format, Object... args) {
+ Log.e("DDMS Menu Bar", String.format(format, args));
+ }
- item = new MenuItem(fileMenu, SWT.NONE);
- item.setText("E&xit\tCtrl-Q");
- item.setAccelerator('Q' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
- item.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- shell.close();
+ public void onPreferencesMenuSelected() {
+ PrefsDialog.run(shell);
+ }
+
+ public void onAboutMenuSelected() {
+ AboutDialog dlg = new AboutDialog(shell);
+ dlg.open();
}
});
+ if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
+ new MenuItem(fileMenu, SWT.SEPARATOR);
+
+ item = new MenuItem(fileMenu, SWT.NONE);
+ item.setText("E&xit\tCtrl-Q");
+ item.setAccelerator('Q' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
+ item.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ shell.close();
+ }
+ });
+ }
+
// create edit menu items
mCopyMenuItem = new MenuItem(editMenu, SWT.NONE);
mCopyMenuItem.setText("&Copy\tCtrl-C");
@@ -900,32 +900,6 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
}
});
- // create Help menu items
- item = new MenuItem(helpMenu, SWT.NONE);
- item.setText("&Contents...");
- item.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- int style = SWT.APPLICATION_MODAL | SWT.OK;
- MessageBox msgBox = new MessageBox(shell, style);
- msgBox.setText("Help!");
- msgBox.setMessage("Help wanted.");
- msgBox.open();
- }
- });
-
- new MenuItem(helpMenu, SWT.SEPARATOR);
-
- item = new MenuItem(helpMenu, SWT.NONE);
- item.setText("&About...");
- item.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- AboutDialog dlg = new AboutDialog(shell);
- dlg.open();
- }
- });
-
// tell the shell to use this menu
shell.setMenuBar(menuBar);
}
@@ -1007,7 +981,9 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
e.y = Math.max(Math.min(e.y, bottom), 100);
if (e.y != sashRect.y) {
sashData.top = new FormAttachment(0, e.y);
- prefs.setValue(PREFERENCE_LOGSASH, e.y);
+ if (prefs != null) {
+ prefs.setValue(PREFERENCE_LOGSASH, e.y);
+ }
panelArea.layout();
}
}
@@ -1199,7 +1175,9 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
e.x = Math.max(Math.min(e.x, right), minPanelWidth);
if (e.x != sashRect.x) {
sashData.left = new FormAttachment(0, e.x);
- prefs.setValue(PREFERENCE_SASH, e.x);
+ if (prefs != null) {
+ prefs.setValue(PREFERENCE_SASH, e.x);
+ }
comp.layout();
}
}
@@ -1282,13 +1260,13 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
@Override
public void widgetSelected(SelectionEvent e) {
// disable the other actions and record current index
- for (int i = 0 ; i < mLogLevelActions.length; i++) {
- ToolItemAction a = mLogLevelActions[i];
+ for (int k = 0 ; k < mLogLevelActions.length; k++) {
+ ToolItemAction a = mLogLevelActions[k];
if (a == newAction) {
a.setChecked(true);
// set the log level
- mLogPanel.setCurrentFilterLogLevel(i+2);
+ mLogPanel.setCurrentFilterLogLevel(k+2);
} else {
a.setChecked(false);
}
@@ -1508,9 +1486,19 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
deleteAction.item.setText("Delete"); //$NON-NLS-1$
}
+ ToolItemAction createNewFolderAction = new ToolItemAction(toolBar, SWT.PUSH);
+ createNewFolderAction.item.setToolTipText("New Folder");
+ image = mDdmUiLibLoader.loadImage("add.png", mDisplay); //$NON-NLS-1$
+ if (image != null) {
+ createNewFolderAction.item.setImage(image);
+ } else {
+ // this is for debugging purpose when the icon is missing
+ createNewFolderAction.item.setText("New Folder"); //$NON-NLS-1$
+ }
+
// device explorer
mExplorer = new DeviceExplorer();
- mExplorer.setActions(pushAction, pullAction, deleteAction);
+ mExplorer.setActions(pushAction, pullAction, deleteAction, createNewFolderAction);
pullAction.item.addSelectionListener(new SelectionAdapter() {
@Override
@@ -1536,6 +1524,14 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener {
});
deleteAction.setEnabled(false);
+ createNewFolderAction.item.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ mExplorer.createNewFolderInSelection();
+ }
+ });
+ createNewFolderAction.setEnabled(false);
+
Composite parent = new Composite(mExplorerShell, SWT.NONE);
parent.setLayoutData(new GridData(GridData.FILL_BOTH));
diff --git a/ddms/app/src/images/ddms-128.png b/ddms/app/src/images/ddms-128.png
new file mode 100644
index 0000000..392a8f3
--- /dev/null
+++ b/ddms/app/src/images/ddms-128.png
Binary files differ
diff --git a/ddms/app/src/resources/images/ddms-icon.png b/ddms/app/src/resources/images/ddms-icon.png
deleted file mode 100644
index 6390a2d..0000000
--- a/ddms/app/src/resources/images/ddms-icon.png
+++ /dev/null
Binary files differ
diff --git a/ddms/app/src/resources/images/ddms-logo.png b/ddms/app/src/resources/images/ddms-logo.png
deleted file mode 100644
index 6390a2d..0000000
--- a/ddms/app/src/resources/images/ddms-logo.png
+++ /dev/null
Binary files differ
diff --git a/ddms/libs/ddmlib/.settings/org.eclipse.jdt.core.prefs b/ddms/libs/ddmlib/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/ddms/libs/ddmlib/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/ddms/libs/ddmlib/Android.mk b/ddms/libs/ddmlib/Android.mk
index 8e46edf..978ac19 100644
--- a/ddms/libs/ddmlib/Android.mk
+++ b/ddms/libs/ddmlib/Android.mk
@@ -18,6 +18,7 @@ include $(CLEAR_VARS)
# Only compile source java files in this lib.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
LOCAL_MODULE := ddmlib
diff --git a/ddms/libs/ddmlib/NOTICE b/ddms/libs/ddmlib/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/ddms/libs/ddmlib/NOTICE
@@ -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/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java b/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java
index e965ccd..f697cba 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java
@@ -200,7 +200,7 @@ public final class AndroidDebugBridge {
/**
* Terminates the ddm library. This must be called upon application termination.
*/
- public static void terminate() {
+ public static synchronized void terminate() {
// kill the monitoring services
if (sThis != null && sThis.mDeviceMonitor != null) {
sThis.mDeviceMonitor.stop();
@@ -211,6 +211,8 @@ public final class AndroidDebugBridge {
if (monitorThread != null) {
monitorThread.quit();
}
+
+ sInitialized = false;
}
/**
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java b/ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java
index bfeeea2..5ef5428 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/FileListingService.java
@@ -389,7 +389,7 @@ public final class FileListingService {
* Returns an escaped version of the entry name.
* @param entryName
*/
- private String escape(String entryName) {
+ public static String escape(String entryName) {
return sEscapePattern.matcher(entryName).replaceAll("\\\\$1"); //$NON-NLS-1$
}
}
@@ -745,7 +745,7 @@ public final class FileListingService {
try {
// create the command
- String command = "ls -l " + entry.getFullPath(); //$NON-NLS-1$
+ String command = "ls -l " + entry.getFullEscapedPath(); //$NON-NLS-1$
// create the receiver object that will parse the result from ls
LsReceiver receiver = new LsReceiver(entry, entryList, linkList);
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/SyncException.java b/ddms/libs/ddmlib/src/com/android/ddmlib/SyncException.java
index 1e9b692..5f84f64 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/SyncException.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/SyncException.java
@@ -46,6 +46,8 @@ public class SyncException extends CanceledException {
FILE_READ_ERROR("Reading local file failed!"),
/** attempting to push a directory. */
LOCAL_IS_DIRECTORY("Local path is a directory."),
+ /** attempting to push a non-existent file. */
+ NO_LOCAL_FILE("Local path doesn't exist."),
/** when the target path of a multi file push is a file. */
REMOTE_IS_FILE("Remote path is a file."),
/** receiving too much data from the remove device at once */
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java b/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java
index f112a12..03a68aa 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java
@@ -23,7 +23,6 @@ import com.android.ddmlib.utils.ArrayHelper;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -231,8 +230,6 @@ public final class SyncService {
* if the unique entry is a folder, this should be a folder.
* @param monitor The progress monitor. Cannot be null.
* @throws SyncException
- * @throws FileNotFoundException if the file exists but is a directory, does not exist but
- * cannot be created, or cannot be opened for any other reason.
* @throws IOException
* @throws TimeoutException
*
@@ -240,7 +237,7 @@ public final class SyncService {
* @see #getNullProgressMonitor()
*/
public void pull(FileEntry[] entries, String localPath, ISyncProgressMonitor monitor)
- throws SyncException, FileNotFoundException, IOException, TimeoutException {
+ throws SyncException, IOException, TimeoutException {
// first we check the destination is a directory and exists
File f = new File(localPath);
@@ -271,16 +268,15 @@ public final class SyncService {
* @param localFilename The local destination.
* @param monitor The progress monitor. Cannot be null.
*
- * @throws SyncException
- * @throws IOException
- * @throws FileNotFoundException
- * @throws TimeoutException
+ * @throws IOException in case of an IO exception.
+ * @throws TimeoutException in case of a timeout reading responses from the device.
+ * @throws SyncException in case of a sync exception.
*
* @see FileListingService.FileEntry
* @see #getNullProgressMonitor()
*/
public void pullFile(FileEntry remote, String localFilename, ISyncProgressMonitor monitor)
- throws FileNotFoundException, IOException, SyncException, TimeoutException {
+ throws IOException, SyncException, TimeoutException {
int total = remote.getSizeValue();
monitor.start(total);
@@ -326,14 +322,12 @@ public final class SyncService {
* @param local An array of loca files to push
* @param remote the remote {@link FileEntry} representing a directory.
* @param monitor The progress monitor. Cannot be null.
- * @throws SyncException
- * @throws FileNotFoundException if the file exists but is a directory, does not exist but
- * cannot be created, or cannot be opened for any other reason.
- * @throws IOException
- * @throws TimeoutException
+ * @throws SyncException if file could not be pushed
+ * @throws IOException in case of I/O error on the connection.
+ * @throws TimeoutException in case of a timeout reading responses from the device.
*/
public void push(String[] local, FileEntry remote, ISyncProgressMonitor monitor)
- throws SyncException, FileNotFoundException, IOException, TimeoutException {
+ throws SyncException, IOException, TimeoutException {
if (remote.isDirectory() == false) {
throw new SyncException(SyncError.REMOTE_IS_FILE);
}
@@ -361,18 +355,15 @@ public final class SyncService {
* @param remote The remote filepath.
* @param monitor The progress monitor. Cannot be null.
*
- * @throws SyncException
- * @throws FileNotFoundException if the file exists but is a directory, does not exist but
- * cannot be created, or cannot be opened for any other reason.
- * @throws IOException
- * @throws TimeoutException
- *
+ * @throws SyncException if file could not be pushed
+ * @throws IOException in case of I/O error on the connection.
+ * @throws TimeoutException in case of a timeout reading responses from the device.
*/
public void pushFile(String local, String remote, ISyncProgressMonitor monitor)
- throws SyncException, FileNotFoundException, IOException, TimeoutException {
+ throws SyncException, IOException, TimeoutException {
File f = new File(local);
if (f.exists() == false) {
- throw new FileNotFoundException();
+ throw new SyncException(SyncError.NO_LOCAL_FILE);
}
if (f.isDirectory()) {
@@ -439,16 +430,13 @@ public final class SyncService {
* @param fileListingService a FileListingService object to browse through remote directories.
* @param monitor the progress monitor. Must be started already.
*
- * @throws FileNotFoundException if the file exists but is a directory, does not exist but
- * cannot be created, or cannot be opened for any other reason.
- * @throws IOException
- * @throws SyncException
- * @throws TimeoutException
+ * @throws SyncException if file could not be pushed
+ * @throws IOException in case of I/O error on the connection.
+ * @throws TimeoutException in case of a timeout reading responses from the device.
*/
private void doPull(FileEntry[] entries, String localPath,
FileListingService fileListingService,
- ISyncProgressMonitor monitor) throws SyncException, FileNotFoundException, IOException,
- TimeoutException {
+ ISyncProgressMonitor monitor) throws SyncException, IOException, TimeoutException {
for (FileEntry e : entries) {
// check if we're cancelled
@@ -484,15 +472,12 @@ public final class SyncService {
* @param remotePath the remote file (length max is 1024)
* @param localPath the local destination
* @param monitor the monitor. The monitor must be started already.
- * @throws FileNotFoundException if the file exists but is a directory, does not exist but
- * cannot be created, or cannot be opened for any other reason.
- * @throws IOException
- * @throws SyncException
- * @throws TimeoutException
+ * @throws SyncException if file could not be pushed
+ * @throws IOException in case of I/O error on the connection.
+ * @throws TimeoutException in case of a timeout reading responses from the device.
*/
private void doPullFile(String remotePath, String localPath,
- ISyncProgressMonitor monitor) throws FileNotFoundException, IOException, SyncException,
- TimeoutException {
+ ISyncProgressMonitor monitor) throws IOException, SyncException, TimeoutException {
byte[] msg = null;
byte[] pullResult = new byte[8];
@@ -581,14 +566,12 @@ public final class SyncService {
* @param remotePath
* @param monitor
*
- * @throws SyncException
- * @throws FileNotFoundException if the file exists but is a directory, does not exist but
- * cannot be created, or cannot be opened for any other reason.
- * @throws IOException
- * @throws TimeoutException
+ * @throws SyncException if file could not be pushed
+ * @throws IOException in case of I/O error on the connection.
+ * @throws TimeoutException in case of a timeout reading responses from the device.
*/
private void doPush(File[] fileArray, String remotePath, ISyncProgressMonitor monitor)
- throws SyncException, FileNotFoundException, IOException, TimeoutException {
+ throws SyncException, IOException, TimeoutException {
for (File f : fileArray) {
// check if we're canceled
if (monitor.isCanceled() == true) {
@@ -618,15 +601,12 @@ public final class SyncService {
* @param remotePath the remote file (length max is 1024)
* @param monitor the monitor. The monitor must be started already.
*
- * @throws SyncException
- * @throws FileNotFoundException if the file exists but is a directory, does not exist but
- * cannot be created, or cannot be opened for any other reason.
- * @throws IOException
- * @throws TimeoutException
+ * @throws SyncException if file could not be pushed
+ * @throws IOException in case of I/O error on the connection.
+ * @throws TimeoutException in case of a timeout reading responses from the device.
*/
private void doPushFile(String localPath, String remotePath,
- ISyncProgressMonitor monitor) throws SyncException, FileNotFoundException, IOException,
- TimeoutException {
+ ISyncProgressMonitor monitor) throws SyncException, IOException, TimeoutException {
FileInputStream fis = null;
byte[] msg;
@@ -712,7 +692,7 @@ public final class SyncService {
* @param result the current adb result. Must contain both FAIL and the length of the message.
* @param timeOut
* @return
- * @throws TimeoutException
+ * @throws TimeoutException in case of a timeout reading responses from the device.
* @throws IOException
*/
private String readErrorMessage(byte[] result, final int timeOut) throws TimeoutException,
@@ -739,7 +719,7 @@ public final class SyncService {
* @return an Integer containing the mode if all went well or null
* otherwise
* @throws IOException
- * @throws TimeoutException
+ * @throws TimeoutException in case of a timeout reading responses from the device.
*/
private Integer readMode(String path) throws TimeoutException, IOException {
// create the stat request message.
diff --git a/ddms/libs/ddmuilib/.classpath b/ddms/libs/ddmuilib/.classpath
index 2cd368c..839e2be 100644
--- a/ddms/libs/ddmuilib/.classpath
+++ b/ddms/libs/ddmuilib/.classpath
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry excluding="Makefile|resources/" kind="src" path="src"/>
- <classpathentry kind="src" path="src/resources"/>
+ <classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
diff --git a/ddms/libs/ddmuilib/.settings/org.eclipse.jdt.core.prefs b/ddms/libs/ddmuilib/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/ddms/libs/ddmuilib/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/ddms/libs/ddmuilib/Android.mk b/ddms/libs/ddmuilib/Android.mk
index 7059e5e..6b50f82 100644
--- a/ddms/libs/ddmuilib/Android.mk
+++ b/ddms/libs/ddmuilib/Android.mk
@@ -1,4 +1,28 @@
# Copyright 2007 The Android Open Source Project
#
-DDMUILIB_LOCAL_DIR := $(call my-dir)
-include $(DDMUILIB_LOCAL_DIR)/src/Android.mk
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Only compile source java files in this lib.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_JAR_MANIFEST := etc/manifest.txt
+
+# If the dependency list is changed, etc/manifest.txt
+# MUST be updated as well (Except for swt.jar which is dynamically
+# added based on whether the VM is 32 or 64 bit)
+LOCAL_JAVA_LIBRARIES := \
+ ddmlib \
+ swt \
+ org.eclipse.jface_3.4.2.M20090107-0800 \
+ org.eclipse.equinox.common_3.4.0.v20080421-2006 \
+ org.eclipse.core.commands_3.4.0.I20080509-2000 \
+ jcommon-1.0.12 \
+ jfreechart-1.0.9 \
+ jfreechart-1.0.9-swt
+
+LOCAL_MODULE := ddmuilib
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/ddms/libs/ddmuilib/NOTICE b/ddms/libs/ddmuilib/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/ddms/libs/ddmuilib/NOTICE
@@ -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/ddms/libs/ddmuilib/src/Android.mk b/ddms/libs/ddmuilib/src/Android.mk
deleted file mode 100644
index cfc1791..0000000
--- a/ddms/libs/ddmuilib/src/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright 2007 The Android Open Source Project
-#
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_JAVA_RESOURCE_DIRS := resources
-
-LOCAL_JAR_MANIFEST := ../etc/manifest.txt
-
-# If the dependency list is changed, etc/manifest.txt
-# MUST be updated as well (Except for swt.jar which is dynamically
-# added based on whether the VM is 32 or 64 bit)
-LOCAL_JAVA_LIBRARIES := \
- ddmlib \
- swt \
- org.eclipse.jface_3.4.2.M20090107-0800 \
- org.eclipse.equinox.common_3.4.0.v20080421-2006 \
- org.eclipse.core.commands_3.4.0.I20080509-2000 \
- jcommon-1.0.12 \
- jfreechart-1.0.9 \
- jfreechart-1.0.9-swt
-
-LOCAL_MODULE := ddmuilib
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/ImageLoader.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/ImageLoader.java
index 40cbd1d..fd480f6 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/ImageLoader.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/ImageLoader.java
@@ -140,13 +140,11 @@ public class ImageLoader {
if (imageStream != null) {
img = new Image(display, imageStream);
- if (img == null) {
- throw new NullPointerException("couldn't load " + tmp);
- }
-
mLoadedImages.put(filename, img);
+ }
- return img;
+ if (img == null) {
+ throw new RuntimeException("Failed to load " + tmp);
}
}
@@ -159,7 +157,6 @@ public class ImageLoader {
* Extra parameters allows for creation of a replacement image of the
* loading failed.
*
- * @param loader the image loader used.
* @param display the Display object
* @param fileName the file name
* @param width optional width to create replacement Image. If -1, null be
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/ScreenShotDialog.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/ScreenShotDialog.java
index 590a8ab..d0c8a2f 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/ScreenShotDialog.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/ScreenShotDialog.java
@@ -43,6 +43,7 @@ import org.eclipse.swt.widgets.Shell;
import java.io.File;
import java.io.IOException;
+import java.util.Calendar;
/**
@@ -57,6 +58,8 @@ public class ScreenShotDialog extends Dialog {
private RawImage mRawImage;
private Clipboard mClipboard;
+ /** Number of 90 degree rotations applied to the current image */
+ private int mRotateCount = 0;
/**
* Create with default style.
@@ -118,6 +121,14 @@ public class ScreenShotDialog extends Dialog {
@Override
public void widgetSelected(SelectionEvent e) {
updateDeviceImage(shell);
+ // RawImage only allows us to rotate the image 90 degrees at the time,
+ // so to preserve the current rotation we must call getRotated()
+ // the same number of times the user has done it manually.
+ // TODO: improve the RawImage class.
+ for (int i=0; i < mRotateCount; i++) {
+ mRawImage = mRawImage.getRotated();
+ }
+ updateImageDisplay(shell);
}
});
@@ -131,6 +142,7 @@ public class ScreenShotDialog extends Dialog {
@Override
public void widgetSelected(SelectionEvent e) {
if (mRawImage != null) {
+ mRotateCount = (mRotateCount + 1) % 4;
mRawImage = mRawImage.getRotated();
updateImageDisplay(shell);
}
@@ -282,10 +294,13 @@ public class ScreenShotDialog extends Dialog {
*/
private void saveImage(Shell shell) {
FileDialog dlg = new FileDialog(shell, SWT.SAVE);
- String fileName;
+
+ Calendar now = Calendar.getInstance();
+ String fileName = String.format("device-%tF-%tH%tM%tS.png",
+ now, now, now, now);
dlg.setText("Save image...");
- dlg.setFileName("device.png");
+ dlg.setFileName(fileName);
String lastDir = DdmUiPreferences.getStore().getString("lastImageSaveDir");
if (lastDir.length() == 0) {
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/explorer/DeviceExplorer.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/explorer/DeviceExplorer.java
index a0febeb..a466be1 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/explorer/DeviceExplorer.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/explorer/DeviceExplorer.java
@@ -19,23 +19,28 @@ package com.android.ddmuilib.explorer;
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.DdmConstants;
import com.android.ddmlib.FileListingService;
+import com.android.ddmlib.FileListingService.FileEntry;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.SyncException;
import com.android.ddmlib.SyncService;
-import com.android.ddmlib.TimeoutException;
-import com.android.ddmlib.FileListingService.FileEntry;
import com.android.ddmlib.SyncService.ISyncProgressMonitor;
+import com.android.ddmlib.TimeoutException;
import com.android.ddmuilib.DdmUiPreferences;
import com.android.ddmuilib.ImageLoader;
import com.android.ddmuilib.Panel;
import com.android.ddmuilib.SyncProgressHelper;
-import com.android.ddmuilib.TableHelper;
import com.android.ddmuilib.SyncProgressHelper.SyncRunnable;
+import com.android.ddmuilib.TableHelper;
import com.android.ddmuilib.actions.ICommonAction;
import com.android.ddmuilib.console.DdmConsole;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
@@ -96,6 +101,7 @@ public class DeviceExplorer extends Panel {
private ICommonAction mPushAction;
private ICommonAction mPullAction;
private ICommonAction mDeleteAction;
+ private ICommonAction mCreateNewFolderAction;
private Image mFileImage;
private Image mFolderImage;
@@ -134,12 +140,14 @@ public class DeviceExplorer extends Panel {
* @param pushAction
* @param pullAction
* @param deleteAction
+ * @param createNewFolderAction
*/
public void setActions(ICommonAction pushAction, ICommonAction pullAction,
- ICommonAction deleteAction) {
+ ICommonAction deleteAction, ICommonAction createNewFolderAction) {
mPushAction = pushAction;
mPullAction = pullAction;
mDeleteAction = deleteAction;
+ mCreateNewFolderAction = createNewFolderAction;
}
/**
@@ -201,6 +209,7 @@ public class DeviceExplorer extends Panel {
mPullAction.setEnabled(false);
mPushAction.setEnabled(false);
mDeleteAction.setEnabled(false);
+ mCreateNewFolderAction.setEnabled(false);
return;
}
if (sel instanceof IStructuredSelection) {
@@ -212,7 +221,9 @@ public class DeviceExplorer extends Panel {
mPullAction.setEnabled(true);
mPushAction.setEnabled(selection.size() == 1);
if (selection.size() == 1) {
- setDeleteEnabledState((FileEntry)element);
+ FileEntry entry = (FileEntry) element;
+ setDeleteEnabledState(entry);
+ mCreateNewFolderAction.setEnabled(entry.isDirectory());
} else {
mDeleteAction.setEnabled(false);
}
@@ -617,6 +628,82 @@ public class DeviceExplorer extends Panel {
}
+ public void createNewFolderInSelection() {
+ TreeItem[] items = mTree.getSelection();
+
+ if (items.length != 1) {
+ return;
+ }
+
+ final FileEntry entry = (FileEntry) items[0].getData();
+
+ if (entry.isDirectory()) {
+ InputDialog inputDialog = new InputDialog(mTree.getShell(), "New Folder",
+ "Please enter the new folder name", "New Folder", new IInputValidator() {
+ public String isValid(String newText) {
+ if ((newText != null) && (newText.length() > 0)
+ && (newText.trim().length() > 0)
+ && (newText.indexOf('/') == -1)
+ && (newText.indexOf('\\') == -1)) {
+ return null;
+ } else {
+ return "Invalid name";
+ }
+ }
+ });
+ inputDialog.open();
+ String value = inputDialog.getValue();
+
+ if (value != null) {
+ // create the mkdir command
+ String command = "mkdir " + entry.getFullEscapedPath() //$NON-NLS-1$
+ + FileListingService.FILE_SEPARATOR + FileEntry.escape(value);
+
+ try {
+ mCurrentDevice.executeShellCommand(command, new IShellOutputReceiver() {
+
+ public boolean isCancelled() {
+ return false;
+ }
+
+ public void flush() {
+ mTreeViewer.refresh(entry);
+ }
+
+ public void addOutput(byte[] data, int offset, int length) {
+ String errorMessage;
+ if (data != null) {
+ errorMessage = new String(data);
+ } else {
+ errorMessage = "";
+ }
+ Status status = new Status(IStatus.ERROR,
+ "DeviceExplorer", 0, errorMessage, null); //$NON-NLS-1$
+ ErrorDialog.openError(mTree.getShell(), "New Folder Error",
+ "New Folder Error", status);
+ }
+ });
+ } catch (TimeoutException e) {
+ // adb failed somehow, we do nothing. We should be
+ // displaying the error from the output of the shell
+ // command.
+ } catch (AdbCommandRejectedException e) {
+ // adb failed somehow, we do nothing. We should be
+ // displaying the error from the output of the shell
+ // command.
+ } catch (ShellCommandUnresponsiveException e) {
+ // adb failed somehow, we do nothing. We should be
+ // displaying the error from the output of the shell
+ // command.
+ } catch (IOException e) {
+ // adb failed somehow, we do nothing. We should be
+ // displaying the error from the output of the shell
+ // command.
+ }
+ }
+ }
+ }
+
/**
* Force a full refresh of the explorer.
*/
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java
index f6e2f19..10680f7 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java
@@ -17,14 +17,14 @@
package com.android.ddmuilib.handler;
import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData.IMethodProfilingHandler;
import com.android.ddmlib.DdmConstants;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
import com.android.ddmlib.SyncException;
import com.android.ddmlib.SyncService;
-import com.android.ddmlib.TimeoutException;
-import com.android.ddmlib.ClientData.IMethodProfilingHandler;
import com.android.ddmlib.SyncService.ISyncProgressMonitor;
+import com.android.ddmlib.TimeoutException;
import com.android.ddmuilib.DdmUiPreferences;
import com.android.ddmuilib.SyncProgressHelper;
import com.android.ddmuilib.SyncProgressHelper.SyncRunnable;
@@ -126,7 +126,7 @@ public class MethodProfilingHandler extends BaseFileHandler
SyncProgressHelper.run(new SyncRunnable() {
public void run(ISyncProgressMonitor monitor)
throws SyncException, IOException, TimeoutException {
- sync.pullFile(tempPath, remoteFilePath, monitor);
+ sync.pullFile(remoteFilePath, tempPath, monitor);
}
public void close() {
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/EditFilterDialog.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/EditFilterDialog.java
index 8bbc7c5..6cd44d0 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/EditFilterDialog.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/EditFilterDialog.java
@@ -43,7 +43,10 @@ import org.eclipse.swt.widgets.Text;
public class EditFilterDialog extends Dialog {
private static final int DLG_WIDTH = 400;
- private static final int DLG_HEIGHT = 250;
+ private static final int DLG_HEIGHT = 260;
+
+ private static final String IMAGE_WARNING = "warning.png"; //$NON-NLS-1$
+ private static final String IMAGE_EMPTY = "empty.png"; //$NON-NLS-1$
private Shell mParent;
@@ -68,6 +71,8 @@ public class EditFilterDialog extends Dialog {
private Button mOkButton;
+ private Label mNameWarning;
+ private Label mTagWarning;
private Label mPidWarning;
public EditFilterDialog(Shell parent) {
@@ -151,7 +156,7 @@ public class EditFilterDialog extends Dialog {
// top part with the filter name
Composite nameComposite = new Composite(mShell, SWT.NONE);
nameComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
- nameComposite.setLayout(new GridLayout(2, false));
+ nameComposite.setLayout(new GridLayout(3, false));
Label l = new Label(nameComposite, SWT.NONE);
l.setText("Filter Name:");
@@ -172,6 +177,10 @@ public class EditFilterDialog extends Dialog {
}
});
+ mNameWarning = new Label(nameComposite, SWT.NONE);
+ mNameWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage(IMAGE_EMPTY,
+ mShell.getDisplay()));
+
// separator
l = new Label(mShell, SWT.SEPARATOR | SWT.HORIZONTAL);
l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
@@ -192,9 +201,8 @@ public class EditFilterDialog extends Dialog {
tagText.setText(mTag);
}
}
- GridData gd = new GridData(GridData.FILL_HORIZONTAL);
- gd.horizontalSpan = 2;
- tagText.setLayoutData(gd);
+
+ tagText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
tagText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
mTag = tagText.getText().trim();
@@ -202,6 +210,10 @@ public class EditFilterDialog extends Dialog {
}
});
+ mTagWarning = new Label(main, SWT.NONE);
+ mTagWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage(IMAGE_EMPTY,
+ mShell.getDisplay()));
+
l = new Label(main, SWT.NONE);
l.setText("by pid:");
@@ -223,14 +235,14 @@ public class EditFilterDialog extends Dialog {
});
mPidWarning = new Label(main, SWT.NONE);
- mPidWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage("empty.png", //$NON-NLS-1$
+ mPidWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage(IMAGE_EMPTY,
mShell.getDisplay()));
l = new Label(main, SWT.NONE);
l.setText("by Log level:");
final Combo logCombo = new Combo(main, SWT.DROP_DOWN | SWT.READ_ONLY);
- gd = new GridData(GridData.FILL_HORIZONTAL);
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalSpan = 2;
logCombo.setLayoutData(gd);
@@ -324,26 +336,58 @@ public class EditFilterDialog extends Dialog {
*/
private void validate() {
+ boolean result = true;
+
// then we check it only contains digits.
if (mPid != null) {
if (mPid.matches("[0-9]*") == false) { //$NON-NLS-1$
- mOkButton.setEnabled(false);
mPidWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage(
- "warning.png", //$NON-NLS-1$
+ IMAGE_WARNING,
mShell.getDisplay()));
- return;
+ mPidWarning.setToolTipText("PID must be a number"); //$NON-NLS-1$
+ result = false;
} else {
mPidWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage(
- "empty.png", //$NON-NLS-1$
+ IMAGE_EMPTY,
mShell.getDisplay()));
+ mPidWarning.setToolTipText(null);
}
}
- if (mName == null || mName.length() == 0) {
- mOkButton.setEnabled(false);
- return;
+ // then we check it not contains character | or :
+ if (mTag != null) {
+ if (mTag.matches(".*[:|].*") == true) { //$NON-NLS-1$
+ mTagWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage(
+ IMAGE_WARNING,
+ mShell.getDisplay()));
+ mTagWarning.setToolTipText("Tag cannot contain | or :"); //$NON-NLS-1$
+ result = false;
+ } else {
+ mTagWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage(
+ IMAGE_EMPTY,
+ mShell.getDisplay()));
+ mTagWarning.setToolTipText(null);
+ }
+ }
+
+ // then we check it not contains character | or :
+ if (mName != null && mName.length() > 0) {
+ if (mName.matches(".*[:|].*") == true) { //$NON-NLS-1$
+ mNameWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage(
+ IMAGE_WARNING,
+ mShell.getDisplay()));
+ mNameWarning.setToolTipText("Name cannot contain | or :"); //$NON-NLS-1$
+ result = false;
+ } else {
+ mNameWarning.setImage(ImageLoader.getDdmUiLibLoader().loadImage(
+ IMAGE_EMPTY,
+ mShell.getDisplay()));
+ mNameWarning.setToolTipText(null);
+ }
+ } else {
+ result = false;
}
- mOkButton.setEnabled(true);
+ mOkButton.setEnabled(result);
}
}
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogFilter.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogFilter.java
index 2f2cfef..74a5e37 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogFilter.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogFilter.java
@@ -79,7 +79,7 @@ public class LogFilter {
private LogColors mColors;
private boolean mTempFilteringStatus = false;
-
+
private final ArrayList<LogMessage> mMessages = new ArrayList<LogMessage>();
private final ArrayList<LogMessage> mNewMessages = new ArrayList<LogMessage>();
@@ -283,7 +283,7 @@ public class LogFilter {
}
mIsCurrentTabItem = selected;
}
-
+
/**
* Adds a new message and optionally removes an old message.
* <p/>The new message is filtered through {@link #accept(LogMessage)}.
@@ -301,7 +301,7 @@ public class LogFilter {
mMessages.remove(index);
mRemovedMessageCount++;
}
-
+
// now we look for it in mNewMessages. This can happen if the new message is added
// and then removed because too many messages are added between calls to #flush()
index = mNewMessages.indexOf(oldMessage);
@@ -322,7 +322,7 @@ public class LogFilter {
return filter;
}
}
-
+
/**
* Removes all the items in the filter and its {@link Table}.
*/
@@ -332,7 +332,7 @@ public class LogFilter {
mMessages.clear();
mTable.removeAll();
}
-
+
/**
* Filters a message.
* @param logMessage the Message
@@ -401,13 +401,13 @@ public class LogFilter {
// if scroll bar is at the bottom, we will scroll
ScrollBar bar = mTable.getVerticalBar();
boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
-
+
// if we are not going to scroll, get the current first item being shown.
int topIndex = mTable.getTopIndex();
// disable drawing
mTable.setRedraw(false);
-
+
int totalCount = mNewMessages.size();
try {
@@ -415,11 +415,12 @@ public class LogFilter {
for (int i = 0 ; i < mRemovedMessageCount && mTable.getItemCount() > 0 ; i++) {
mTable.remove(0);
}
-
+ mRemovedMessageCount = 0;
+
if (mUnreadCount > mTable.getItemCount()) {
mUnreadCount = mTable.getItemCount();
}
-
+
// add the new items
for (int i = 0 ; i < totalCount ; i++) {
LogMessage msg = mNewMessages.get(i);
@@ -430,7 +431,7 @@ public class LogFilter {
// but at least ddms won't crash.
Log.e("LogFilter", e);
}
-
+
// redraw
mTable.setRedraw(true);
@@ -467,7 +468,7 @@ public class LogFilter {
mTabItem.setText(mName); //$NON-NLS-1$
}
}
-
+
mNewMessages.clear();
}
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java
index 80e24d3..80ed6e9 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogPanel.java
@@ -1211,13 +1211,13 @@ public class LogPanel extends SelectionDependentPanel {
} else {
messageIndex = mBufferEnd;
+ // increment the next usable slot index
+ mBufferEnd = (mBufferEnd + 1) % STRING_BUFFER_LENGTH;
+
// check we aren't overwriting start
if (mBufferEnd == mBufferStart) {
mBufferStart = (mBufferStart + 1) % STRING_BUFFER_LENGTH;
}
-
- // increment the next usable slot index
- mBufferEnd = (mBufferEnd + 1) % STRING_BUFFER_LENGTH;
}
LogMessage oldMessage = null;
diff --git a/ddms/libs/ddmuilib/src/resources/images/add.png b/ddms/libs/ddmuilib/src/images/add.png
index eefc2ca..eefc2ca 100644
--- a/ddms/libs/ddmuilib/src/resources/images/add.png
+++ b/ddms/libs/ddmuilib/src/images/add.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/android.png b/ddms/libs/ddmuilib/src/images/android.png
index 3779d4d..3779d4d 100644
--- a/ddms/libs/ddmuilib/src/resources/images/android.png
+++ b/ddms/libs/ddmuilib/src/images/android.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/backward.png b/ddms/libs/ddmuilib/src/images/backward.png
index 90a9713..90a9713 100644
--- a/ddms/libs/ddmuilib/src/resources/images/backward.png
+++ b/ddms/libs/ddmuilib/src/images/backward.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/capture.png b/ddms/libs/ddmuilib/src/images/capture.png
index da5c10b..da5c10b 100644
--- a/ddms/libs/ddmuilib/src/resources/images/capture.png
+++ b/ddms/libs/ddmuilib/src/images/capture.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/clear.png b/ddms/libs/ddmuilib/src/images/clear.png
index 0009cf6..0009cf6 100644
--- a/ddms/libs/ddmuilib/src/resources/images/clear.png
+++ b/ddms/libs/ddmuilib/src/images/clear.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/d.png b/ddms/libs/ddmuilib/src/images/d.png
index d45506e..d45506e 100644
--- a/ddms/libs/ddmuilib/src/resources/images/d.png
+++ b/ddms/libs/ddmuilib/src/images/d.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/debug-attach.png b/ddms/libs/ddmuilib/src/images/debug-attach.png
index 9b8a11c..9b8a11c 100644
--- a/ddms/libs/ddmuilib/src/resources/images/debug-attach.png
+++ b/ddms/libs/ddmuilib/src/images/debug-attach.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/debug-error.png b/ddms/libs/ddmuilib/src/images/debug-error.png
index f22da1f..f22da1f 100644
--- a/ddms/libs/ddmuilib/src/resources/images/debug-error.png
+++ b/ddms/libs/ddmuilib/src/images/debug-error.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/debug-wait.png b/ddms/libs/ddmuilib/src/images/debug-wait.png
index 322be63..322be63 100644
--- a/ddms/libs/ddmuilib/src/resources/images/debug-wait.png
+++ b/ddms/libs/ddmuilib/src/images/debug-wait.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/delete.png b/ddms/libs/ddmuilib/src/images/delete.png
index db5fab8..db5fab8 100644
--- a/ddms/libs/ddmuilib/src/resources/images/delete.png
+++ b/ddms/libs/ddmuilib/src/images/delete.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/device.png b/ddms/libs/ddmuilib/src/images/device.png
index 7dbbbb6..7dbbbb6 100644
--- a/ddms/libs/ddmuilib/src/resources/images/device.png
+++ b/ddms/libs/ddmuilib/src/images/device.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/down.png b/ddms/libs/ddmuilib/src/images/down.png
index f9426cb..f9426cb 100644
--- a/ddms/libs/ddmuilib/src/resources/images/down.png
+++ b/ddms/libs/ddmuilib/src/images/down.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/e.png b/ddms/libs/ddmuilib/src/images/e.png
index dee7c97..dee7c97 100644
--- a/ddms/libs/ddmuilib/src/resources/images/e.png
+++ b/ddms/libs/ddmuilib/src/images/e.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/edit.png b/ddms/libs/ddmuilib/src/images/edit.png
index b8f65bc..b8f65bc 100644
--- a/ddms/libs/ddmuilib/src/resources/images/edit.png
+++ b/ddms/libs/ddmuilib/src/images/edit.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/empty.png b/ddms/libs/ddmuilib/src/images/empty.png
index f021542..f021542 100644
--- a/ddms/libs/ddmuilib/src/resources/images/empty.png
+++ b/ddms/libs/ddmuilib/src/images/empty.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/emulator.png b/ddms/libs/ddmuilib/src/images/emulator.png
index a718042..a718042 100644
--- a/ddms/libs/ddmuilib/src/resources/images/emulator.png
+++ b/ddms/libs/ddmuilib/src/images/emulator.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/file.png b/ddms/libs/ddmuilib/src/images/file.png
index 043a814..043a814 100644
--- a/ddms/libs/ddmuilib/src/resources/images/file.png
+++ b/ddms/libs/ddmuilib/src/images/file.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/folder.png b/ddms/libs/ddmuilib/src/images/folder.png
index 7e29b1a..7e29b1a 100644
--- a/ddms/libs/ddmuilib/src/resources/images/folder.png
+++ b/ddms/libs/ddmuilib/src/images/folder.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/forward.png b/ddms/libs/ddmuilib/src/images/forward.png
index a97a605..a97a605 100644
--- a/ddms/libs/ddmuilib/src/resources/images/forward.png
+++ b/ddms/libs/ddmuilib/src/images/forward.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/gc.png b/ddms/libs/ddmuilib/src/images/gc.png
index 5194806..5194806 100644
--- a/ddms/libs/ddmuilib/src/resources/images/gc.png
+++ b/ddms/libs/ddmuilib/src/images/gc.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/halt.png b/ddms/libs/ddmuilib/src/images/halt.png
index 10e3720..10e3720 100644
--- a/ddms/libs/ddmuilib/src/resources/images/halt.png
+++ b/ddms/libs/ddmuilib/src/images/halt.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/heap.png b/ddms/libs/ddmuilib/src/images/heap.png
index e3aa3f0..e3aa3f0 100644
--- a/ddms/libs/ddmuilib/src/resources/images/heap.png
+++ b/ddms/libs/ddmuilib/src/images/heap.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/hprof.png b/ddms/libs/ddmuilib/src/images/hprof.png
index 123d062..123d062 100644
--- a/ddms/libs/ddmuilib/src/resources/images/hprof.png
+++ b/ddms/libs/ddmuilib/src/images/hprof.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/i.png b/ddms/libs/ddmuilib/src/images/i.png
index 98385c5..98385c5 100644
--- a/ddms/libs/ddmuilib/src/resources/images/i.png
+++ b/ddms/libs/ddmuilib/src/images/i.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/importBug.png b/ddms/libs/ddmuilib/src/images/importBug.png
index f5da179..f5da179 100644
--- a/ddms/libs/ddmuilib/src/resources/images/importBug.png
+++ b/ddms/libs/ddmuilib/src/images/importBug.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/load.png b/ddms/libs/ddmuilib/src/images/load.png
index 9e7bf6e..9e7bf6e 100644
--- a/ddms/libs/ddmuilib/src/resources/images/load.png
+++ b/ddms/libs/ddmuilib/src/images/load.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/pause.png b/ddms/libs/ddmuilib/src/images/pause.png
index 19d286d..19d286d 100644
--- a/ddms/libs/ddmuilib/src/resources/images/pause.png
+++ b/ddms/libs/ddmuilib/src/images/pause.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/play.png b/ddms/libs/ddmuilib/src/images/play.png
index d54f013..d54f013 100644
--- a/ddms/libs/ddmuilib/src/resources/images/play.png
+++ b/ddms/libs/ddmuilib/src/images/play.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/pull.png b/ddms/libs/ddmuilib/src/images/pull.png
index f48f1b1..f48f1b1 100644
--- a/ddms/libs/ddmuilib/src/resources/images/pull.png
+++ b/ddms/libs/ddmuilib/src/images/pull.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/push.png b/ddms/libs/ddmuilib/src/images/push.png
index 6222864..6222864 100644
--- a/ddms/libs/ddmuilib/src/resources/images/push.png
+++ b/ddms/libs/ddmuilib/src/images/push.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/save.png b/ddms/libs/ddmuilib/src/images/save.png
index 040ebda..040ebda 100644
--- a/ddms/libs/ddmuilib/src/resources/images/save.png
+++ b/ddms/libs/ddmuilib/src/images/save.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/sort_down.png b/ddms/libs/ddmuilib/src/images/sort_down.png
index 2d4ccc1..2d4ccc1 100644
--- a/ddms/libs/ddmuilib/src/resources/images/sort_down.png
+++ b/ddms/libs/ddmuilib/src/images/sort_down.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/sort_up.png b/ddms/libs/ddmuilib/src/images/sort_up.png
index 3a0bc3c..3a0bc3c 100644
--- a/ddms/libs/ddmuilib/src/resources/images/sort_up.png
+++ b/ddms/libs/ddmuilib/src/images/sort_up.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/thread.png b/ddms/libs/ddmuilib/src/images/thread.png
index ac839e8..ac839e8 100644
--- a/ddms/libs/ddmuilib/src/resources/images/thread.png
+++ b/ddms/libs/ddmuilib/src/images/thread.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/tracing_start.png b/ddms/libs/ddmuilib/src/images/tracing_start.png
index 88771cc..88771cc 100644
--- a/ddms/libs/ddmuilib/src/resources/images/tracing_start.png
+++ b/ddms/libs/ddmuilib/src/images/tracing_start.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png b/ddms/libs/ddmuilib/src/images/tracing_stop.png
index 71bd215..71bd215 100644
--- a/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png
+++ b/ddms/libs/ddmuilib/src/images/tracing_stop.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/up.png b/ddms/libs/ddmuilib/src/images/up.png
index 92edf5a..92edf5a 100644
--- a/ddms/libs/ddmuilib/src/resources/images/up.png
+++ b/ddms/libs/ddmuilib/src/images/up.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/v.png b/ddms/libs/ddmuilib/src/images/v.png
index 8044051..8044051 100644
--- a/ddms/libs/ddmuilib/src/resources/images/v.png
+++ b/ddms/libs/ddmuilib/src/images/v.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/w.png b/ddms/libs/ddmuilib/src/images/w.png
index 129d0f9..129d0f9 100644
--- a/ddms/libs/ddmuilib/src/resources/images/w.png
+++ b/ddms/libs/ddmuilib/src/images/w.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/warning.png b/ddms/libs/ddmuilib/src/images/warning.png
index ca3b6ed..ca3b6ed 100644
--- a/ddms/libs/ddmuilib/src/resources/images/warning.png
+++ b/ddms/libs/ddmuilib/src/images/warning.png
Binary files differ
diff --git a/draw9patch/Android.mk b/draw9patch/Android.mk
index 934495d..f8de8d6 100644
--- a/draw9patch/Android.mk
+++ b/draw9patch/Android.mk
@@ -12,6 +12,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-DRAW9PATCH_LOCAL_DIR := $(call my-dir)
-include $(DRAW9PATCH_LOCAL_DIR)/etc/Android.mk
-include $(DRAW9PATCH_LOCAL_DIR)/src/Android.mk
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_JAR_MANIFEST := etc/manifest.txt
+
+LOCAL_JAVA_LIBRARIES := \
+ swing-worker-1.1
+
+LOCAL_MODULE := draw9patch
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/draw9patch/NOTICE b/draw9patch/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/draw9patch/NOTICE
@@ -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/draw9patch/src/com/android/draw9patch/ui/MainFrame.java b/draw9patch/src/com/android/draw9patch/ui/MainFrame.java
index d5b6409..57f6cd9 100644
--- a/draw9patch/src/com/android/draw9patch/ui/MainFrame.java
+++ b/draw9patch/src/com/android/draw9patch/ui/MainFrame.java
@@ -40,9 +40,15 @@ public class MainFrame extends JFrame {
private JMenuItem saveMenuItem;
private ImageEditorPanel imageEditor;
+ private static final String TITLE_FORMAT = "Draw 9-patch: %s";
+
public MainFrame(String path) throws HeadlessException {
super("Draw 9-patch");
+ if (path != null) {
+ setTitle(String.format(TITLE_FORMAT, path));
+ }
+
buildActions();
buildMenuBar();
buildContent();
@@ -164,6 +170,7 @@ public class MainFrame extends JFrame {
protected void done() {
try {
showImageEditor(get(), file.getAbsolutePath());
+ setTitle(String.format(TITLE_FORMAT, file.getAbsolutePath()));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
diff --git a/draw9patch/src/resources/images/checker.png b/draw9patch/src/images/checker.png
index 78908f4..78908f4 100644
--- a/draw9patch/src/resources/images/checker.png
+++ b/draw9patch/src/images/checker.png
Binary files differ
diff --git a/draw9patch/src/resources/images/drop.png b/draw9patch/src/images/drop.png
index 7a7436a..7a7436a 100644
--- a/draw9patch/src/resources/images/drop.png
+++ b/draw9patch/src/images/drop.png
Binary files differ
diff --git a/dumpeventlog/NOTICE b/dumpeventlog/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/dumpeventlog/NOTICE
@@ -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/eclipse/changes.txt b/eclipse/changes.txt
index 5c76898..17c43fd 100644
--- a/eclipse/changes.txt
+++ b/eclipse/changes.txt
@@ -1,3 +1,105 @@
+11.0.0
+- Visual Refactoring:
+ - The new "Extract Style" refactoring pulls out style constants and
+ defines them as style rules intead.
+ - The new "Wrap in Container" refactoring surrounds the selected
+ views with a new layout, and transfers namespace and layout
+ parameters to the new parent
+ - The new "Change Widget Type" refactoring changes the type of the
+ selected views to a new type. (Also, a new selection context menu
+ in the visual layout editor makes it easy to select siblings as
+ well as views anywhere in the layout that have the same type).
+ - The new "Change Layout" refactoring changes layouts from one type
+ to another, and can also flatten a layout hierarchy.
+ - The "Extract as Include" refactoring now finds identical fragments
+ in other layouts and offers to combine all into a single include.
+ - There is a new Refactoring Quick Assistant which can be invoked
+ from the XML editor (with Ctrl-1) to apply any of the above
+ refactorings (and Extract String) to the current selection.
+- Visual Layout Editor:
+ - Improved "rendering fidelity": The layout preview has been
+ improved and should more closely match the rendering on actual
+ devices.
+ - The visual editor now previews ListViews at designtime. By
+ default, a two-line list item is shown, but with a context menu
+ you can pick any arbitrary layout to be used for the list items,
+ and you can also pick the header and footer layouts.
+ - The palette now supports "configurations" where a single view is
+ presented in various different configurations. For example, there
+ is a whole "Textfields" palette category where the EditText view
+ can be dragged in as a password field, an e-mail field, a phone
+ field, and so on. Similarly, TextViews are offered preconfigured
+ with large, normal and small theme sizes, and LinearLayouts are
+ offered both in horizontal and vertical configurations.
+ - The palette supports custom views, picking up any custom
+ implementations of the View class in your project source folders
+ or in included libraries, and these can be dragged into layouts.
+ - The layout editor automatically applies a "zoom to fit" for newly
+ opened files as well as on device size and orientation changes to
+ ensure that large layouts are always fully visible unless you
+ manually zoom in.
+ - You can drop an "include" tag from the palette, which will pop up
+ a layout chooser, and the chosen layout is added as an include.
+ Similarly, dropping images or image buttons will pop up image
+ resource choosers to initialize the new image with.
+ - The configuration chooser now applies the "Render Target" and
+ "Locale" settings project wide, making it trivial to check the
+ layouts for different languages or render targets without having
+ to configure these individually for each layout.
+ - The layout editor is smarter about picking a default theme to
+ render a layout with, consulting factors like theme registrations
+ in the manifest, the SDK version, etc.
+- XML editors:
+ - Code completion has been significantly improved. It now works
+ within <style> elements, it completes dimensional units,
+ it sorts resource paths in values based on the attribute name,
+ etc. There are also many fixes to handle text replacement.
+ - AAPT errors are handled better. They are now underlined for the
+ relevant range in the editor, and a new quickfix makes it trivial
+ to create missing resources.
+ - Code completion for drawable, animation and color XML files.
+- DDMS:
+ - "New Folder" action in the File Explorer
+ - The screenshot dialog will add timestamps to the filenames, and
+ preserve the orientation on snapshot refresh
+- TraceView: Mouse-wheel zoom support in the timeline
+- The New Android Project wizard now supports Eclipse working sets
+- Most of the tools have improved integration with the Mac OSX
+ system menu bar.
+- Most of the tools have new launcher icons.
+
+10.0.1
+- Temporary work-around to resolve the rare cases in which the layout
+ editor will not open.
+- Fix issue in which ADT 10.0.0 would install on Eclipse 3.4 and
+ lower, even though ADT requires Eclipse 3.5 or higher (as of
+ 10.0.0).
+
+10.0.0
+- The tools now automatically generate Java Programming Language
+ source files (in the gen/ directory) and bytecode (in the res/raw/
+ directory) from your .rs files.
+- A Binary XML editor has been added.
+- Traceview is now integrated into the Eclipse UI.
+- The "Go To Declaration" feature for XML and .java files quickly show
+ all the matches in the project and allows you jump to specific items
+ such as string translations or onClick handlers.
+- The Resource Chooser can create items such as dimensions, integers,
+ ids, and booleans.
+- Improvements to the Visual Layout Editor:
+ - A new Palette with categories and rendering previews.
+ - A Layout Actions bar that provides quick access to common layout
+ operations.
+ - When the Android 3.0 rendering library is selected, layouts render
+ more like they do on devices. This includes rendering of status
+ and title bars to more accurately reflect the actual screen space
+ available to applications.
+ - Zoom improvements such as fit to view, persistent scale, and
+ keyboard access..
+ - Further improvements to <merge> layouts, as well as layouts with
+ gesture overlays.
+ - Improved rendering error diagnostics.
+
9.0.0
- Visual Layout Editor
- Empty layouts with 0,0 size are now automatically expanded when
diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt
index 235e9f2..9291f47 100644
--- a/eclipse/dictionary.txt
+++ b/eclipse/dictionary.txt
@@ -16,6 +16,8 @@ apk
app
apps
arg
+async
+attr
attrs
avd
avds
@@ -63,8 +65,10 @@ dex
dexified
diff
diffs
+dir
dirs
ditto
+docs
dpi
drawable
drawables
@@ -80,9 +84,11 @@ foreach
fqcn
framelayout
gen
+git
groovy
guava
hardcoded
+hardcodes
hotfix
href
http
@@ -99,6 +105,8 @@ inline
instanceof
instantiatable
int
+interpolator
+interpolators
iterable
javac
javadoc
@@ -118,15 +126,20 @@ lowercase
luminance
mac
macs
+malformed
marquee
metadata
min
+mipmap
monte
+ms
+msg
multi
multimap
multimaps
namespace
namespaces
+newfound
num
ok
os
@@ -136,11 +149,13 @@ pings
placeholder
plugin
popup
+popups
pre
precompiler
pref
prefs
preload
+preloaded
preloads
primordial
printf
@@ -149,12 +164,14 @@ programmatically
proguard
proxies
proxy
+quickfix
recompilation
rect
redo
refactor
refactoring
regexp
+regexps
registry
reindent
remap
@@ -185,6 +202,8 @@ stderr
stdout
stretchiness
struct
+styleable
+styleables
subclassing
submenu
supertype
@@ -201,9 +220,11 @@ tooltip
tooltips
traceview
translucency
+typo
ui
uncomment
undescribed
+undoable
unhide
unicode
uninstall
diff --git a/eclipse/features/com.android.ide.eclipse.adt/feature.xml b/eclipse/features/com.android.ide.eclipse.adt/feature.xml
index 2676c32..aeea750 100644
--- a/eclipse/features/com.android.ide.eclipse.adt/feature.xml
+++ b/eclipse/features/com.android.ide.eclipse.adt/feature.xml
@@ -2,7 +2,7 @@
<feature
id="com.android.ide.eclipse.adt"
label="Android Development Tools"
- version="10.0.0.qualifier"
+ version="11.0.0.qualifier"
provider-name="The Android Open Source Project"
plugin="com.android.ide.eclipse.adt">
@@ -113,7 +113,7 @@ This Agreement is governed by the laws of the State of New York and the intellec
<requires>
<import plugin="com.android.ide.eclipse.ddms" match="perfect"/>
- <import plugin="org.eclipse.core.runtime"/>
+ <import plugin="org.eclipse.core.runtime" version="3.5" match="greaterOrEqual"/>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.debug.core"/>
<import plugin="org.eclipse.debug.ui"/>
@@ -127,7 +127,7 @@ This Agreement is governed by the laws of the State of New York and the intellec
<import plugin="org.eclipse.ui.workbench.texteditor"/>
<import plugin="org.eclipse.ui.console"/>
<import plugin="org.eclipse.core.filesystem"/>
- <import plugin="org.eclipse.ui"/>
+ <import plugin="org.eclipse.ui" version="3.5" match="greaterOrEqual"/>
<import plugin="org.eclipse.ui.ide"/>
<import plugin="org.eclipse.ui.forms"/>
<import plugin="org.eclipse.gef"/>
diff --git a/eclipse/features/com.android.ide.eclipse.ddms/feature.xml b/eclipse/features/com.android.ide.eclipse.ddms/feature.xml
index 31ef379..cc13984 100644
--- a/eclipse/features/com.android.ide.eclipse.ddms/feature.xml
+++ b/eclipse/features/com.android.ide.eclipse.ddms/feature.xml
@@ -2,7 +2,7 @@
<feature
id="com.android.ide.eclipse.ddms"
label="Android DDMS"
- version="10.0.0.qualifier"
+ version="11.0.0.qualifier"
provider-name="The Android Open Source Project"
plugin="com.android.ide.eclipse.ddms">
@@ -228,8 +228,8 @@
</url>
<requires>
- <import plugin="org.eclipse.ui"/>
- <import plugin="org.eclipse.core.runtime"/>
+ <import plugin="org.eclipse.ui" version="3.5" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.core.runtime" version="3.5" match="greaterOrEqual"/>
<import plugin="org.eclipse.ui.console"/>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.ui.ide"/>
diff --git a/eclipse/features/com.android.ide.eclipse.hierarchyviewer/feature.xml b/eclipse/features/com.android.ide.eclipse.hierarchyviewer/feature.xml
index e2e869b..9323cb0 100644
--- a/eclipse/features/com.android.ide.eclipse.hierarchyviewer/feature.xml
+++ b/eclipse/features/com.android.ide.eclipse.hierarchyviewer/feature.xml
@@ -2,7 +2,7 @@
<feature
id="com.android.ide.eclipse.hierarchyviewer"
label="Android Hierarchy Viewer"
- version="10.0.0.qualifier"
+ version="11.0.0.qualifier"
provider-name="The Android Open Source Project"
plugin="com.android.ide.eclipse.hierarchyviewer">
@@ -223,8 +223,8 @@
</url>
<requires>
- <import plugin="org.eclipse.ui"/>
- <import plugin="org.eclipse.core.runtime"/>
+ <import plugin="org.eclipse.ui" version="3.5" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.core.runtime" version="3.5" match="greaterOrEqual"/>
<import plugin="org.eclipse.ui.console"/>
<import plugin="com.android.ide.eclipse.ddms" match="perfect"/>
</requires>
diff --git a/eclipse/features/com.android.ide.eclipse.pdt/feature.xml b/eclipse/features/com.android.ide.eclipse.pdt/feature.xml
index f1e767b..06eadb2 100644
--- a/eclipse/features/com.android.ide.eclipse.pdt/feature.xml
+++ b/eclipse/features/com.android.ide.eclipse.pdt/feature.xml
@@ -2,7 +2,7 @@
<feature
id="com.android.ide.eclipse.pdt"
label="Android Platform Development Tools"
- version="10.0.0.qualifier"
+ version="11.0.0.qualifier"
provider-name="The Android Open Source Project">
<description>
diff --git a/eclipse/features/com.android.ide.eclipse.tests/feature.xml b/eclipse/features/com.android.ide.eclipse.tests/feature.xml
index d8d5e63..1fb4ee7 100644
--- a/eclipse/features/com.android.ide.eclipse.tests/feature.xml
+++ b/eclipse/features/com.android.ide.eclipse.tests/feature.xml
@@ -2,7 +2,7 @@
<feature
id="com.android.ide.eclipse.tests"
label="ADT Tests"
- version="10.0.0.qualifier"
+ version="11.0.0.qualifier"
provider-name="The Android Open Source Project">
<copyright>
diff --git a/eclipse/features/com.android.ide.eclipse.traceview/feature.xml b/eclipse/features/com.android.ide.eclipse.traceview/feature.xml
index bebc92a..4eceaa5 100644
--- a/eclipse/features/com.android.ide.eclipse.traceview/feature.xml
+++ b/eclipse/features/com.android.ide.eclipse.traceview/feature.xml
@@ -2,7 +2,7 @@
<feature
id="com.android.ide.eclipse.traceview"
label="Android Traceview"
- version="10.0.0.qualifier"
+ version="11.0.0.qualifier"
provider-name="The Android Open Source Project"
plugin="com.android.ide.eclipse.traceview">
@@ -223,8 +223,8 @@
</url>
<requires>
- <import plugin="org.eclipse.ui"/>
- <import plugin="org.eclipse.core.runtime"/>
+ <import plugin="org.eclipse.ui" version="3.5" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.core.runtime" version="3.5" match="greaterOrEqual"/>
<import plugin="org.eclipse.ui.ide"/>
<import plugin="com.android.ide.eclipse.ddms" version="10.0.0" match="greaterOrEqual"/>
<import plugin="org.eclipse.core.filesystem"/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.eclipse.jdt.core.prefs b/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..f4696e4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 16 15:10:40 PDT 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs b/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs
index ecb8e78..a1c97ef 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs
@@ -1,4 +1,4 @@
-#Wed Nov 24 07:39:01 PST 2010
+#Mon Feb 28 13:14:31 PST 2011
eclipse.preferences.version=1
-org.moreunit.unitsourcefolder=adt\:src\:adt-tests\:unittests
+org.moreunit.unitsourcefolder=adt\:src\:adt-tests\:unittests\#adt\:src\:adt-tests\:src
org.moreunit.useprojectsettings=true
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index bea92fe..5214736 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -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: 10.0.0.qualifier
+Bundle-Version: 11.0.0.qualifier
Bundle-ClassPath: .,
libs/androidprefs.jar,
libs/sdklib.jar,
@@ -47,7 +47,8 @@ Require-Bundle: com.android.ide.eclipse.ddms,
org.eclipse.ltk.ui.refactoring,
org.eclipse.core.expressions
Eclipse-LazyStart: true
-Export-Package: com.android.annotations;x-friends:="com.android.ide.eclipse.tests",
+Export-Package: com.android,
+ com.android.annotations;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.common.api;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.common.layout;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.common.log;x-friends:="com.android.ide.eclipse.tests",
@@ -55,6 +56,7 @@ Export-Package: com.android.annotations;x-friends:="com.android.ide.eclipse.test
com.android.ide.common.rendering.api;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.common.rendering.legacy;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.common.resources;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.common.resources.configuration;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.common.resources.platform;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.common.sdk;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests",
@@ -63,6 +65,7 @@ Export-Package: com.android.annotations;x-friends:="com.android.ide.eclipse.test
com.android.ide.eclipse.adt.internal.build;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.build.builders;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.editors.binaryxml,
com.android.ide.eclipse.adt.internal.editors.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.export;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.layout;x-friends:="com.android.ide.eclipse.tests",
@@ -70,6 +73,7 @@ Export-Package: com.android.annotations;x-friends:="com.android.ide.eclipse.test
com.android.ide.eclipse.adt.internal.editors.layout.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.layout.gle2;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.layout.gre;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.editors.layout.refactoring,
com.android.ide.eclipse.adt.internal.editors.layout.uimodel;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.manifest;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.manifest.descriptors;x-friends:="com.android.ide.eclipse.tests",
@@ -96,7 +100,6 @@ Export-Package: com.android.annotations;x-friends:="com.android.ide.eclipse.test
com.android.ide.eclipse.adt.internal.refactorings.extractstring;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.refactorings.renamepackage;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.resources;x-friends:="com.android.ide.eclipse.tests",
- com.android.ide.eclipse.adt.internal.resources.configurations;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.resources.manager;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.sdk;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.sourcelookup;x-friends:="com.android.ide.eclipse.tests",
@@ -106,6 +109,7 @@ Export-Package: com.android.annotations;x-friends:="com.android.ide.eclipse.test
com.android.ide.eclipse.adt.internal.wizards.newproject;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.wizards.newxmlfile;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.io;x-friends:="com.android.ide.eclipse.tests",
+ com.android.io;x-friends:="com.android.ide.eclipse.tests",
com.android.layoutlib.api;x-friends:="com.android.ide.eclipse.tests",
com.android.ninepatch;x-friends:="com.android.ide.eclipse.tests",
com.android.prefs;x-friends:="com.android.ide.eclipse.tests",
@@ -117,7 +121,6 @@ Export-Package: com.android.annotations;x-friends:="com.android.ide.eclipse.test
com.android.sdklib.internal.export;x-friends:="com.android.ide.eclipse.tests",
com.android.sdklib.internal.project;x-friends:="com.android.ide.eclipse.tests",
com.android.sdklib.internal.repository;x-friends:="com.android.ide.eclipse.tests",
- com.android.sdklib.io;x-friends:="com.android.ide.eclipse.tests",
com.android.sdklib.repository;x-friends:="com.android.ide.eclipse.tests",
com.android.sdklib.util;x-friends:="com.android.ide.eclipse.tests",
com.android.sdklib.xml;x-friends:="com.android.ide.eclipse.tests",
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/about.ini b/eclipse/plugins/com.android.ide.eclipse.adt/about.ini
index 560e475..2d7cdaa 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/about.ini
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/about.ini
@@ -1,2 +1,2 @@
aboutText=%blurb
-featureImage=icons/android_32x32.png \ No newline at end of file
+featureImage=icons/android-32.png \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/SearchView.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/SearchView.png
index c41a2cd..2b45fc2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/icons/SearchView.png
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/SearchView.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/VerticalLinearLayout.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/VerticalLinearLayout.png
new file mode 100644
index 0000000..e03c16e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/VerticalLinearLayout.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/android-32.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/android-32.png
new file mode 100644
index 0000000..4e0cc13
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/android-32.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/android-64.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/android-64.png
new file mode 100644
index 0000000..2c6f149
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/android-64.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/android_32x32.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/android_32x32.png
deleted file mode 100644
index a382aad..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/icons/android_32x32.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/android_large.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/android_large.png
deleted file mode 100644
index e0ca992..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/icons/android_large.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/attribute.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/attribute.png
new file mode 100644
index 0000000..04508ea
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/attribute.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/draw9patch-16.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/draw9patch-16.png
new file mode 100644
index 0000000..cc080e7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/draw9patch-16.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/element.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/element.png
new file mode 100644
index 0000000..e5cd0ca
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/element.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/refresh.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/refresh.png
new file mode 100644
index 0000000..7cbebf4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/refresh.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png
new file mode 100644
index 0000000..205a032
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/view_menu.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/view_menu.png
new file mode 100644
index 0000000..8575107
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/view_menu.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index 551e6be..a16a766 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -49,6 +49,11 @@
<super type="org.eclipse.core.resources.textmarker"/>
<persistent value="true"/>
</extension>
+ <extension point="org.eclipse.ui.ide.markerResolution">
+ <markerResolutionGenerator
+ markerType="com.android.ide.eclipse.common.aaptProblem"
+ class="com.android.ide.eclipse.adt.internal.build.AaptQuickFix"/>
+ </extension>
<extension
id="ResourceManagerBuilder"
name="Android Resource Manager"
@@ -146,7 +151,7 @@
modes="debug, run"
name="Android Application"
public="true"
- sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
+ sourceLocatorId="com.android.ide.eclipse.adt.internal.sourcelookup.AdtSourceLookupDirector"
sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer">
</launchConfigurationType>
</extension>
@@ -527,6 +532,41 @@
id="com.android.ide.eclipse.editors.xml.XmlEditor"
name="Android Xml Resources Editor">
</editor>
+ <editor
+ class="com.android.ide.eclipse.adt.internal.editors.animator.AnimationEditor"
+ default="false"
+ extensions="xml"
+ icon="icons/android_file.png"
+ id="com.android.ide.eclipse.editors.animator.AnimationEditor"
+ name="Android Animation Editor">
+ </editor>
+ <editor
+ class="com.android.ide.eclipse.adt.internal.editors.drawable.DrawableEditor"
+ default="false"
+ extensions="xml"
+ icon="icons/android_file.png"
+ id="com.android.ide.eclipse.editors.drawable.DrawableEditor"
+ name="Android Drawable Editor">
+ </editor>
+ <editor
+ class="com.android.ide.eclipse.adt.internal.editors.color.ColorEditor"
+ default="false"
+ extensions="xml"
+ icon="icons/android_file.png"
+ id="com.android.ide.eclipse.editors.color.ColorEditor"
+ name="Android Color Editor">
+ </editor>
+ <editor
+ class="com.android.ide.eclipse.adt.internal.editors.binaryxml.BinaryXMLMultiPageEditorPart"
+ contributorClass="org.eclipse.wst.xml.ui.internal.tabletree.XMLMultiPageEditorActionBarContributor"
+ icon="$nl$/icons/android_file.png"
+ id="com.android.ide.eclipse.adt.binedit.BinaryXMLMultiPageEditorPart"
+ name="Android Binary XML editor"
+ symbolicFontName="org.eclipse.wst.sse.ui.textfont">
+ <contentTypeBinding
+ contentTypeId="com.android.ide.eclipse.adt.binaryXml">
+ </contentTypeBinding>
+ </editor>
</extension>
<extension
point="org.eclipse.ui.views">
@@ -534,7 +574,7 @@
allowMultiple="false"
category="com.android.ide.eclipse.ddms.views.category"
class="com.android.ide.eclipse.adt.internal.ui.ResourceExplorerView"
- icon="icons/android.png"
+ icon="icons/draw9patch-16.png"
id="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView"
name="Resource Explorer">
</view>
@@ -561,6 +601,26 @@
class="com.android.ide.eclipse.adt.internal.editors.xml.XmlSourceViewerConfig"
target="com.android.ide.eclipse.editors.xml.XmlEditor">
</sourceViewerConfiguration>
+ <sourceViewerConfiguration
+ class="com.android.ide.eclipse.adt.internal.editors.animator.AnimationSourceViewerConfig"
+ target="com.android.ide.eclipse.editors.animator.AnimationEditor">
+ </sourceViewerConfiguration>
+ <sourceViewerConfiguration
+ class="com.android.ide.eclipse.adt.internal.editors.drawable.DrawableSourceViewerConfig"
+ target="com.android.ide.eclipse.editors.drawable.DrawableEditor">
+ </sourceViewerConfiguration>
+ <sourceViewerConfiguration
+ class="com.android.ide.eclipse.adt.internal.editors.color.ColorSourceViewerConfig"
+ target="com.android.ide.eclipse.editors.color.ColorEditor">
+ </sourceViewerConfiguration>
+ <provisionalConfiguration
+ type="org.eclipse.jface.text.quickassist.IQuickAssistProcessor"
+ class="com.android.ide.eclipse.adt.internal.build.AaptQuickFix"
+ target="org.eclipse.wst.xml.XML_DEFAULT" />
+ <provisionalConfiguration
+ type="org.eclipse.jface.text.quickassist.IQuickAssistProcessor"
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.RefactoringAssistant"
+ target="org.eclipse.wst.xml.XML_DEFAULT" />
</extension>
<extension
point="org.eclipse.ui.propertyPages">
@@ -652,6 +712,51 @@
style="push"
tooltip="Extracts a string into Android resource string">
</action>
+ <action
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractIncludeAction"
+ definitionId="com.android.ide.eclipse.adt.refactoring.extract.include"
+ id="com.android.ide.eclipse.adt.actions.ExtractInclude"
+ label="Extract as Include..."
+ menubarPath="org.eclipse.jdt.ui.refactoring.menu/com.android.ide.eclipse.adt.refactoring.menu/android"
+ style="push"
+ tooltip="Extracts Views as Included Layout">
+ </action>
+ <action
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractStyleAction"
+ definitionId="com.android.ide.eclipse.adt.refactoring.extract.style"
+ id="com.android.ide.eclipse.adt.actions.ExtractStyle"
+ label="Extract Style..."
+ menubarPath="org.eclipse.jdt.ui.refactoring.menu/com.android.ide.eclipse.adt.refactoring.menu/android"
+ style="push"
+ tooltip="Extracts Styles">
+ </action>
+ <action
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.WrapInAction"
+ definitionId="com.android.ide.eclipse.adt.refactoring.wrapin"
+ id="com.android.ide.eclipse.adt.actions.WrapIn"
+ label="Wrap In Container..."
+ menubarPath="org.eclipse.jdt.ui.refactoring.menu/com.android.ide.eclipse.adt.refactoring.menu/android"
+ style="push"
+ tooltip="Wraps Views in a new container">
+ </action>
+ <action
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeLayoutAction"
+ definitionId="com.android.ide.eclipse.adt.refactoring.convert"
+ id="com.android.ide.eclipse.adt.actions.ChangeLayout"
+ label="Change Layout..."
+ menubarPath="org.eclipse.jdt.ui.refactoring.menu/com.android.ide.eclipse.adt.refactoring.menu/android"
+ style="push"
+ tooltip="Changes layouts from one type to another">
+ </action>
+ <action
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeViewAction"
+ definitionId="com.android.ide.eclipse.adt.refactoring.changeview"
+ id="com.android.ide.eclipse.adt.actions.ChangeView"
+ label="Change Widget Type..."
+ menubarPath="org.eclipse.jdt.ui.refactoring.menu/com.android.ide.eclipse.adt.refactoring.menu/android"
+ style="push"
+ tooltip="Changes the type of the selected widgets">
+ </action>
<menu
id="org.eclipse.jdt.ui.refactoring.menu"
label="Refactor">
@@ -761,6 +866,36 @@
id="com.android.ide.eclipse.adt.refactoring.extract.string"
name="Extract Android String">
</command>
+ <command
+ categoryId="com.android.ide.eclipse.adt.refactoring.category"
+ description="Extract Views as Included Layout"
+ id="com.android.ide.eclipse.adt.refactoring.extract.include"
+ name="Extract as Include">
+ </command>
+ <command
+ categoryId="com.android.ide.eclipse.adt.refactoring.category"
+ description="Extract Styles"
+ id="com.android.ide.eclipse.adt.refactoring.extract.style"
+ name="Extract Styles">
+ </command>
+ <command
+ categoryId="com.android.ide.eclipse.adt.refactoring.category"
+ description="Wraps Views in a New Container"
+ id="com.android.ide.eclipse.adt.refactoring.wrapin"
+ name="Wrap in Container">
+ </command>
+ <command
+ categoryId="com.android.ide.eclipse.adt.refactoring.category"
+ description="Converts Layouts from One Type to Another"
+ id="com.android.ide.eclipse.adt.refactoring.convert"
+ name="Change Layout">
+ </command>
+ <command
+ categoryId="com.android.ide.eclipse.adt.refactoring.category"
+ description="Changes the widget type for the selection"
+ id="com.android.ide.eclipse.adt.refactoring.changeview"
+ name="Change Widget Type">
+ </command>
</extension>
<extension
point="org.eclipse.ltk.core.refactoring.refactoringContributions">
@@ -768,6 +903,26 @@
class="com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringContribution"
id="com.android.ide.eclipse.adt.refactoring.extract.string">
</contribution>
+ <contribution
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractIncludeContribution"
+ id="com.android.ide.eclipse.adt.refactoring.extract.include">
+ </contribution>
+ <contribution
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractStyleContribution"
+ id="com.android.ide.eclipse.adt.refactoring.extract.style">
+ </contribution>
+ <contribution
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.WrapInContribution"
+ id="com.android.ide.eclipse.adt.refactoring.wrapin">
+ </contribution>
+ <contribution
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeLayoutContribution"
+ id="com.android.ide.eclipse.adt.refactoring.convert">
+ </contribution>
+ <contribution
+ class="com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeViewContribution"
+ id="com.android.ide.eclipse.adt.refactoring.changeview">
+ </contribution>
</extension>
<extension
point="org.eclipse.core.expressions.propertyTesters">
@@ -910,25 +1065,14 @@
commandId="com.android.ide.eclipse.adt.launch.LaunchShortcut.run"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/>
</extension>
- <extension point="org.eclipse.core.contenttype.contentTypes">
- <content-type id="com.android.ide.eclipse.adt.binaryXml" name="Android Binary XML"
- priority="high"
- file-extensions="xml">
- <describer
- class="com.android.ide.eclipse.adt.internal.editors.binaryxml.BinaryXMLDescriber">
- </describer>
+ <!-- NOTE: deactivated temporarily due to bug 15003.
+ extension point="org.eclipse.core.contenttype.contentTypes">
+ <content-type
+ describer="com.android.ide.eclipse.adt.internal.editors.binaryxml.BinaryXMLDescriber"
+ file-extensions="xml"
+ id="com.android.ide.eclipse.adt.binaryXml"
+ name="Android Binary XML"
+ priority="high">
</content-type>
- </extension>
- <extension point="org.eclipse.ui.editors">
- <editor
- name="Android Binary XML editor"
- icon="$nl$/icons/android_file.png"
- contributorClass="org.eclipse.wst.xml.ui.internal.tabletree.XMLMultiPageEditorActionBarContributor"
- class="com.android.ide.eclipse.adt.internal.editors.binaryxml.BinaryXMLMultiPageEditorPart"
- symbolicFontName="org.eclipse.wst.sse.ui.textfont"
- id="com.android.ide.eclipse.adt.binedit.BinaryXMLMultiPageEditorPart">
- <contentTypeBinding
- contentTypeId="com.android.ide.eclipse.adt.binaryXml" />
- </editor>
- </extension>
+ </extension -->
</plugin>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/DrawingStyle.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/DrawingStyle.java
index 0938afa..7317ebc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/DrawingStyle.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/DrawingStyle.java
@@ -44,6 +44,12 @@ public enum DrawingStyle {
HOVER,
/**
+ * The style used for hovered views (e.g. when the mouse is directly on top
+ * of the view), when the hover happens to be the same object as the selection
+ */
+ HOVER_SELECTION,
+
+ /**
* The style used to draw anchors (lines to the other views the given view
* is anchored to)
*/
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java
index efd8086..6aa4776 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java
@@ -19,6 +19,8 @@ package com.android.ide.common.api;
import com.android.annotations.Nullable;
+import java.util.Collection;
+
/**
* A Client Rules Engine is a set of methods that {@link IViewRule}s can use to
* access the client public API of the Rules Engine. Rules can access it via
@@ -133,5 +135,22 @@ public interface IClientRulesEngine {
* right, top and bottom margins respectively
*/
String[] displayMarginInput(String all, String left, String right, String top, String bottom);
+
+ /**
+ * Displays an input dialog tailored for inputing the source of an {@code <include>}
+ * layout tag. This is similar to {@link #displayResourceInput} for resource type
+ * "layout", but should also attempt to filter out layout resources that cannot be
+ * included from the current context (because it would result in a cyclic dependency).
+ *
+ * @return the layout resource to include
+ */
+ String displayIncludeSourceInput();
+
+ /**
+ * Select the given nodes
+ *
+ * @param nodes the nodes to be selected, never null
+ */
+ void select(Collection<INode> nodes);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/INode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/INode.java
index 8c2bed1..dd64dfa 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/INode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/INode.java
@@ -128,6 +128,15 @@ public interface INode {
INode insertChildAt(String viewFqcn, int index);
/**
+ * Removes the given XML element child from this node's list of children.
+ * <p/>
+ * This call must be done in the context of editXml().
+ *
+ * @param node The child to be deleted.
+ */
+ void removeChild(INode node);
+
+ /**
* Sets an attribute for the underlying XML element.
* Attributes are not written immediately -- instead the XML editor batches edits and
* then commits them all together at once later.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java
index 15d3a98..c5a4435 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java
@@ -24,6 +24,12 @@ public enum InsertType {
/** The view is newly created (by for example a palette drag) */
CREATE,
+ /**
+ * Same as {@link #CREATE} but when the views are constructed for previewing, for
+ * example as part of a palette drag.
+ */
+ CREATE_PREVIEW,
+
/** The view is being inserted here because it was moved from somewhere else */
MOVE,
@@ -32,4 +38,15 @@ public enum InsertType {
* (including drags, but not from the palette)
*/
PASTE;
+
+ /**
+ * Returns true if this insert type is for a newly created view (for example a by
+ * palette drag). Note that this includes both normal create events as well as well as
+ * views created as part of previewing operations.
+ *
+ * @return true if this {@link InsertType} is for a newly created view
+ */
+ public boolean isCreate() {
+ return this == CREATE || this == CREATE_PREVIEW;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java
index 4af4559..47fcbd7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java
@@ -17,6 +17,7 @@
package com.android.ide.common.layout;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
import com.android.ide.common.api.DrawingStyle;
import com.android.ide.common.api.DropFeedback;
@@ -189,9 +190,9 @@ public class AbsoluteLayoutRule extends BaseLayoutRule {
}
newChild.setAttribute(ANDROID_URI, "layout_x", //$NON-NLS-1$
- x + "dip"); //$NON-NLS-1$
+ String.format(VALUE_N_DP, x));
newChild.setAttribute(ANDROID_URI, "layout_y", //$NON-NLS-1$
- y + "dip"); //$NON-NLS-1$
+ String.format(VALUE_N_DP, y));
addInnerElements(newChild, element, idMap);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
index d21c43d..18ff320 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
@@ -189,7 +189,7 @@ public class BaseViewRule implements IViewRule {
String newText = mRulesEngine.displayResourceInput("string", oldText); //$NON-NLS-1$
if (newText != null) {
node.editXml("Change Text", new PropertySettingNodeHandler(ANDROID_URI,
- ATTR_TEXT, newText));
+ ATTR_TEXT, newText.length() > 0 ? newText : null));
}
}
@@ -659,7 +659,7 @@ public class BaseViewRule implements IViewRule {
public void onChildInserted(INode node, INode parent, InsertType insertType) {
}
- private static String stripIdPrefix(String id) {
+ public static String stripIdPrefix(String id) {
if (id.startsWith(NEW_ID_PREFIX)) {
id = id.substring(NEW_ID_PREFIX.length());
} else if (id.startsWith(ID_PREFIX)) {
@@ -674,21 +674,4 @@ public class BaseViewRule implements IViewRule {
}
return value;
}
-
- private static class PropertySettingNodeHandler implements INodeHandler {
- private final String mNamespaceUri;
- private final String mAttribute;
- private final String mValue;
-
- public PropertySettingNodeHandler(String namespaceUri, String attribute, String value) {
- super();
- mNamespaceUri = namespaceUri;
- mAttribute = attribute;
- mValue = value;
- }
-
- public void handle(INode node) {
- node.setAttribute(mNamespaceUri, mAttribute, mValue);
- }
- }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/CalendarViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/CalendarViewRule.java
new file mode 100644
index 0000000..161cca8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/CalendarViewRule.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 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.common.layout;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
+
+import com.android.ide.common.api.INode;
+import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
+
+/**
+ * An {@link IViewRule} for android.widget.CalendarView.
+ */
+public class CalendarViewRule extends BaseViewRule {
+
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ // CalendarViews need a lot of space, and the wrapping doesn't seem to work
+ // well anyway; it reports a much-to-small size than actually accommodates its
+ // content.
+ node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, getFillParentValueName());
+ node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, getFillParentValueName());
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java
index b3cf5ce..9aa476e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java
@@ -37,7 +37,7 @@ public class DialerFilterRule extends BaseViewRule {
super.onCreate(node, parent, insertType);
// A DialerFilter requires a couple of nested EditTexts with fixed ids:
- if (insertType == InsertType.CREATE) {
+ if (insertType.isCreate()) {
String fillParent = getFillParentValueName();
INode hint = node.appendChild(FQCN_EDIT_TEXT);
hint.setAttribute(ANDROID_URI, ATTR_TEXT, "Hint");
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java
new file mode 100644
index 0000000..dfe9d6f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 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.common.layout;
+
+import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.REQUEST_FOCUS;
+
+import com.android.ide.common.api.IMenuCallback;
+import com.android.ide.common.api.INode;
+import com.android.ide.common.api.INodeHandler;
+import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
+import com.android.ide.common.api.MenuAction;
+
+import java.util.List;
+
+/**
+ * An {@link IViewRule} for android.widget.EditText.
+ */
+public class EditTextRule extends BaseViewRule {
+
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ if (parent != null) {
+ INode focus = findFocus(findRoot(parent));
+ if (focus == null) {
+ // Add <requestFocus>
+ node.appendChild(REQUEST_FOCUS);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Adds a "Request Focus" menu item.
+ */
+ @Override
+ public List<MenuAction> getContextMenu(final INode selectedNode) {
+ final boolean hasFocus = hasFocus(selectedNode);
+ final String label = hasFocus ? "Clear Focus" : "Request Focus";
+
+ IMenuCallback onChange = new IMenuCallback() {
+ public void action(MenuAction menuAction, String valueId, Boolean newValue) {
+ selectedNode.editXml(label, new INodeHandler() {
+ public void handle(INode node) {
+ INode focus = findFocus(findRoot(node));
+ if (focus != null && focus.getParent() != null) {
+ focus.getParent().removeChild(focus);
+ }
+ if (!hasFocus) {
+ node.appendChild(REQUEST_FOCUS);
+ }
+ }
+ });
+ }
+ };
+
+ return concatenate(super.getContextMenu(selectedNode),
+ new MenuAction.Action("_setfocus", label, null, onChange)); //$NON-NLS-1$
+ }
+
+ /** Returns true if the given node currently has focus */
+ private static boolean hasFocus(INode node) {
+ INode focus = findFocus(node);
+ if (focus != null) {
+ return focus.getParent() == node;
+ }
+
+ return false;
+ }
+
+ /** Returns the root/top level node in the view hierarchy that contains the given node */
+ private static INode findRoot(INode node) {
+ // First find the parent
+ INode root = node;
+ while (root != null) {
+ INode parent = root.getParent();
+ if (parent == null) {
+ break;
+ } else {
+ root = parent;
+ }
+ }
+
+ return root;
+ }
+
+ /** Finds the focus node (not the node containing focus, but the actual request focus node
+ * under a given node */
+ private static INode findFocus(INode node) {
+ if (node.getFqcn().equals(REQUEST_FOCUS)) {
+ return node;
+ }
+
+ for (INode child : node.getChildren()) {
+ INode focus = findFocus(child);
+ if (focus != null) {
+ return focus;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java
index 655eee2..b8c7408 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java
@@ -16,7 +16,10 @@
package com.android.ide.common.layout;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_GRAVITY;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
import com.android.ide.common.api.DrawingStyle;
import com.android.ide.common.api.DropFeedback;
@@ -25,10 +28,13 @@ import com.android.ide.common.api.IFeedbackPainter;
import com.android.ide.common.api.IGraphics;
import com.android.ide.common.api.INode;
import com.android.ide.common.api.INodeHandler;
+import com.android.ide.common.api.IViewMetadata;
import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
import com.android.ide.common.api.MenuAction;
import com.android.ide.common.api.Point;
import com.android.ide.common.api.Rect;
+import com.android.ide.common.api.IViewMetadata.FillPreference;
import com.android.util.Pair;
import java.util.List;
@@ -56,7 +62,7 @@ public class FrameLayoutRule extends BaseLayoutRule {
});
}
- void drawFeedback(
+ protected void drawFeedback(
IGraphics gc,
INode targetNode,
IDragElement[] elements,
@@ -155,8 +161,25 @@ public class FrameLayoutRule extends BaseLayoutRule {
super.addLayoutActions(actions, parentNode, children);
actions.add(MenuAction.createSeparator(25));
actions.add(createMarginAction(parentNode, children));
- if (children.size() > 0) {
+ if (children != null && children.size() > 0) {
actions.add(createGravityAction(children, ATTR_LAYOUT_GRAVITY));
}
}
+
+ @Override
+ public void onChildInserted(INode node, INode parent, InsertType insertType) {
+ // Look at the fill preferences and fill embedded layouts etc
+ String fqcn = node.getFqcn();
+ IViewMetadata metadata = mRulesEngine.getMetadata(fqcn);
+ if (metadata != null) {
+ FillPreference fill = metadata.getFillPreference();
+ String fillParent = getFillParentValueName();
+ if (fill.fillHorizontally(true)) {
+ node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
+ }
+ if (fill.fillVertically(false)) {
+ node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
+ }
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java
index 71bd704..62ea6f9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java
@@ -23,9 +23,15 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION;
import static com.android.ide.common.layout.LayoutConstants.FQCN_LINEAR_LAYOUT;
import static com.android.ide.common.layout.LayoutConstants.VALUE_HORIZONTAL;
+import com.android.ide.common.api.DrawingStyle;
+import com.android.ide.common.api.DropFeedback;
+import com.android.ide.common.api.IDragElement;
+import com.android.ide.common.api.IGraphics;
import com.android.ide.common.api.INode;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
+import com.android.ide.common.api.Point;
+import com.android.ide.common.api.Rect;
/**
* An {@link IViewRule} for android.widget.HorizontalScrollView.
@@ -46,7 +52,7 @@ public class HorizontalScrollViewRule extends FrameLayoutRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ if (insertType.isCreate()) {
// Insert a horizontal linear layout which is commonly used with horizontal scrollbars
// as described by the documentation for HorizontalScrollbars.
INode linearLayout = node.appendChild(FQCN_LINEAR_LAYOUT);
@@ -55,4 +61,32 @@ public class HorizontalScrollViewRule extends FrameLayoutRule {
}
}
+ @Override
+ public DropFeedback onDropMove(INode targetNode, IDragElement[] elements,
+ DropFeedback feedback, Point p) {
+ DropFeedback f = super.onDropMove(targetNode, elements, feedback, p);
+
+ // HorizontalScrollViews only allow a single child
+ if (targetNode.getChildren().length > 0) {
+ f.invalidTarget = true;
+ }
+ return f;
+ }
+
+ @Override
+ protected void drawFeedback(
+ IGraphics gc,
+ INode targetNode,
+ IDragElement[] elements,
+ DropFeedback feedback) {
+ if (targetNode.getChildren().length > 0) {
+ Rect b = targetNode.getBounds();
+ if (b.isValid()) {
+ gc.useStyle(DrawingStyle.DROP_RECIPIENT);
+ gc.drawRect(b);
+ }
+ } else {
+ super.drawFeedback(gc, targetNode, elements, feedback);
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java
index 4338a9a..6fceba4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java
@@ -24,7 +24,7 @@ import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
/**
- * An {@link IViewRule} for android.widget.ImageButtonRule.
+ * An {@link IViewRule} for android.widget.ImageButton.
*/
public class ImageButtonRule extends BaseViewRule {
@@ -32,7 +32,19 @@ public class ImageButtonRule extends BaseViewRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ // When dropping an include tag, ask the user which layout to include.
+ if (insertType == InsertType.CREATE) { // NOT InsertType.CREATE_PREVIEW
+ String src = mRulesEngine.displayResourceInput("drawable", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ if (src != null) {
+ node.editXml("Set Image",
+ new PropertySettingNodeHandler(ANDROID_URI, ATTR_SRC,
+ src.length() > 0 ? src : null));
+ return;
+ }
+ }
+
+ // Fallback if dismissed or during previews etc
+ if (insertType.isCreate()) {
node.setAttribute(ANDROID_URI, ATTR_SRC, getSampleImageSrc());
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java
index 9d26e75..a33323e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java
@@ -32,7 +32,19 @@ public class ImageViewRule extends BaseViewRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ // When dropping an include tag, ask the user which layout to include.
+ if (insertType == InsertType.CREATE) { // NOT InsertType.CREATE_PREVIEW
+ String src = mRulesEngine.displayResourceInput("drawable", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ if (src != null) {
+ node.editXml("Set Image",
+ new PropertySettingNodeHandler(ANDROID_URI, ATTR_SRC,
+ src.length() > 0 ? src : null));
+ return;
+ }
+ }
+
+ // Fallback if dismissed or during previews etc
+ if (insertType.isCreate()) {
node.setAttribute(ANDROID_URI, ATTR_SRC, getSampleImageSrc());
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IncludeRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IncludeRule.java
new file mode 100644
index 0000000..a451257
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IncludeRule.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2011 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.common.layout;
+
+import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.ATTR_LAYOUT;
+
+import com.android.ide.common.api.INode;
+import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
+
+/**
+ * An {@link IViewRule} for the special XML {@code <include>} tag.
+ */
+public class IncludeRule extends BaseViewRule {
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ // When dropping an include tag, ask the user which layout to include.
+ if (insertType == InsertType.CREATE) { // NOT InsertType.CREATE_PREVIEW
+ String include = mRulesEngine.displayIncludeSourceInput();
+ if (include != null) {
+ node.editXml("Include Layout",
+ // Note -- the layout attribute is NOT in the Android namespace!
+ new PropertySettingNodeHandler(null, ATTR_LAYOUT,
+ include.length() > 0 ? include : null));
+ } else {
+ // Remove the view; the insertion was canceled
+ parent.removeChild(node);
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
index ee5a8c9..bb04498 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
@@ -16,6 +16,8 @@
package com.android.ide.common.layout;
+import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
+
import com.android.sdklib.SdkConstants;
/**
@@ -27,10 +29,10 @@ import com.android.sdklib.SdkConstants;
* </ul>
*/
public class LayoutConstants {
- /** The element name in a <code>&lt;view class="..."&gt;</code> element. */
+ /** The element name in a {@code <view class="...">} element. */
public static final String VIEW = "view"; //$NON-NLS-1$
- /** The attribute name in a <code>&lt;view class="..."&gt;</code> element. */
+ /** The attribute name in a {@code <view class="...">} element. */
public static final String ATTR_CLASS = "class"; //$NON-NLS-1$
public static final String ATTR_ON_CLICK = "onClick"; //$NON-NLS-1$
@@ -38,17 +40,28 @@ public class LayoutConstants {
public static final String RELATIVE_LAYOUT = "RelativeLayout"; //$NON-NLS-1$
public static final String LINEAR_LAYOUT = "LinearLayout"; //$NON-NLS-1$
public static final String ABSOLUTE_LAYOUT = "AbsoluteLayout"; //$NON-NLS-1$
+ public static final String TABLE_LAYOUT = "TableLayout"; //$NON-NLS-1$
+ public static final String TABLE_ROW = "TableRow"; //$NON-NLS-1$
+ public static final String CALENDAR_VIEW = "CalendarView"; //$NON-NLS-1$
public static final String LIST_VIEW = "ListView"; //$NON-NLS-1$
+ public static final String EDIT_TEXT = "EditText"; //$NON-NLS-1$
public static final String GALLERY = "Gallery"; //$NON-NLS-1$
public static final String GRID_VIEW = "GridView"; //$NON-NLS-1$
public static final String SCROLL_VIEW = "ScrollView"; //$NON-NLS-1$
+ public static final String RADIO_BUTTON = "RadioButton"; //$NON-NLS-1$
+ public static final String RADIO_GROUP = "RadioGroup"; //$NON-NLS-1$
public static final String EXPANDABLE_LIST_VIEW = "ExpandableListView";//$NON-NLS-1$
+ public static final String GESTURE_OVERLAY_VIEW = "GestureOverlayView";//$NON-NLS-1$
+ public static final String HORIZONTAL_SCROLL_VIEW = "HorizontalScrollView"; //$NON-NLS-1$
public static final String ATTR_TEXT = "text"; //$NON-NLS-1$
+ public static final String ATTR_HINT = "hint"; //$NON-NLS-1$
public static final String ATTR_ID = "id"; //$NON-NLS-1$
+ public static final String ATTR_STYLE = "style"; //$NON-NLS-1$
public static final String ATTR_HANDLE = "handle"; //$NON-NLS-1$
public static final String ATTR_CONTENT = "content"; //$NON-NLS-1$
public static final String ATTR_CHECKED = "checked"; //$NON-NLS-1$
+ public static final String ATTR_BACKGROUND = "background"; //$NON-NLS-1$
public static final String ATTR_LAYOUT_PREFIX = "layout_"; //$NON-NLS-1$
public static final String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$
@@ -62,10 +75,16 @@ public class LayoutConstants {
public static final String ATTR_LAYOUT_MARGIN_TOP = "layout_marginTop"; //$NON-NLS-1$
public static final String ATTR_LAYOUT_MARGIN_BOTTOM = "layout_marginBottom"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_ALIGN_LEFT = "layout_alignLeft"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_ALIGN_RIGHT = "layout_alignRight"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_ALIGN_TOP = "layout_alignTop"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_ALIGN_BOTTOM = "layout_alignBottom"; //$NON-NLS-1$
+
public static final String ATTR_LAYOUT_ALIGN_PARENT_TOP = "layout_alignParentTop"; //$NON-NLS-1$
public static final String ATTR_LAYOUT_ALIGN_PARENT_BOTTOM = "layout_alignParentBottom"; //$NON-NLS-1$
public static final String ATTR_LAYOUT_ALIGN_PARENT_LEFT = "layout_alignParentLeft";//$NON-NLS-1$
public static final String ATTR_LAYOUT_ALIGN_PARENT_RIGHT = "layout_alignParentRight"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING = "layout_alignWithParentMissing"; //$NON-NLS-1$
public static final String ATTR_LAYOUT_ALIGN_BASELINE = "layout_alignBaseline"; //$NON-NLS-1$
@@ -84,7 +103,8 @@ public class LayoutConstants {
public static final String VALUE_WRAP_CONTENT = "wrap_content"; //$NON-NLS-1$
public static final String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$
public static final String VALUE_TRUE = "true"; //$NON-NLS-1$
- public static final String VALUE_N_DIP = "%ddip"; //$NON-NLS-1$
+ public static final String VALUE_FALSE= "false"; //$NON-NLS-1$
+ public static final String VALUE_N_DP = "%ddp"; //$NON-NLS-1$
public static final String VALUE_CENTER_VERTICAL = "centerVertical"; //$NON-NLS-1$
public static final String VALUE_CENTER_IN_PARENT = "centerInParent"; //$NON-NLS-1$
@@ -105,8 +125,25 @@ public class LayoutConstants {
public static final String VALUE_ALIGN_WITH_PARENT_MISSING =
"alignWithParentMissing"; //$NON-NLS-1$
+ // Gravity values. These have the GRAVITY_ prefix in front of value because we already
+ // have VALUE_CENTER_HORIZONTAL defined for layouts, and its definition conflicts
+ // (centerHorizontal versus center_horizontal)
+ public static final String GRAVITY_VALUE_ = "center"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_CENTER = "center"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_RIGHT = "right"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_LEFT = "left"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_BOTTOM = "bottom"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_TOP = "top"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_FILL_HORIZONTAL = "fill_horizontal"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_FILL_VERTICAL = "fill_vertical"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_CENTER_HORIZONTAL = "center_horizontal"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_CENTER_VERTICAL = "center_vertical"; //$NON-NLS-1$
+ public static final String GRAVITY_VALUE_FILL = "fill"; //$NON-NLS-1$
+
/** The default prefix used for the {@link #ANDROID_URI} name space */
- public static final String ANDROID_NS_PREFIX = "android"; //$NON-NLS-1$
+ public static final String ANDROID_NS_NAME = "android"; //$NON-NLS-1$
+ /** The default prefix used for the {@link #ANDROID_URI} name space including the colon */
+ public static final String ANDROID_NS_NAME_PREFIX = "android:"; //$NON-NLS-1$
/**
* Namespace for the Android resource XML, i.e.
@@ -114,18 +151,35 @@ public class LayoutConstants {
*/
public static final String ANDROID_URI = SdkConstants.NS_RESOURCES;
+ /**
+ * The package name where the widgets live (the ones that require no prefix in layout
+ * files)
+ */
+ public static final String ANDROID_WIDGET_PREFIX = "android.widget."; //$NON-NLS-1$
+
+ /**
+ * The top level android package as a prefix, "android.".
+ */
+ public static final String ANDROID_PKG_PREFIX = ANDROID_PKG + '.';
+
/** The fully qualified class name of an EditText view */
public static final String FQCN_EDIT_TEXT = "android.widget.EditText"; //$NON-NLS-1$
/** The fully qualified class name of a LinearLayout view */
public static final String FQCN_LINEAR_LAYOUT = "android.widget.LinearLayout"; //$NON-NLS-1$
+ /** The fully qualified class name of a RelativeLayout view */
+ public static final String FQCN_RELATIVE_LAYOUT = "android.widget.RelativeLayout"; //$NON-NLS-1$
+
/** The fully qualified class name of a FrameLayout view */
public static final String FQCN_FRAME_LAYOUT = "android.widget.FrameLayout"; //$NON-NLS-1$
/** The fully qualified class name of a TableRow view */
public static final String FQCN_TABLE_ROW = "android.widget.TableRow"; //$NON-NLS-1$
+ /** The fully qualified class name of a TableLayout view */
+ public static final String FQCN_TABLE_LAYOUT = "android.widget.TableLayout"; //$NON-NLS-1$
+
/** The fully qualified class name of a TabWidget view */
public static final String FQCN_TAB_WIDGET = "android.widget.TabWidget"; //$NON-NLS-1$
@@ -138,6 +192,12 @@ public class LayoutConstants {
/** The fully qualified class name of an AdapterView */
public static final String FQCN_ADAPTER_VIEW = "android.widget.AdapterView"; //$NON-NLS-1$
+ /** The fully qualified class name of a GestureOverlayView */
+ public static final String FQCN_GESTURE_OVERLAY_VIEW = "android.gesture.GestureOverlayView"; //$NON-NLS-1$
+
+ /** The fully qualified class name of a RadioGroup */
+ public static final String FQCN_RADIO_GROUP = "android.widgets.RadioGroup"; //$NON-NLS-1$
+
public static final String ATTR_SRC = "src"; //$NON-NLS-1$
// like fill_parent for API 8
@@ -169,4 +229,7 @@ public class LayoutConstants {
/** Prefix for resources that reference Android strings */
public static String ANDROID_STRING_PREFIX = "@android:string/"; //$NON-NLS-1$
+
+ /** Prefix for resources that reference Android layouts */
+ public static String ANDROID_LAYOUT_PREFIX = "@android:layout/"; //$NON-NLS-1$
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
index 1e2d0e3..4b9d006 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
@@ -530,6 +530,11 @@ public class LinearLayoutRule extends BaseLayoutRule {
String fillParent = getFillParentValueName();
if (fill.fillHorizontally(vertical)) {
node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
+ } else if (!vertical && fill == FillPreference.WIDTH_IN_VERTICAL) {
+ // In a horizontal layout, make views that would fill horizontally in a
+ // vertical layout have a non-zero weight instead. This will make the item
+ // fill but only enough to allow other views to be shown as well.
+ node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, "1"); //$NON-NLS-1$
}
if (fill.fillVertically(vertical)) {
node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java
index aee94bc..c7e21c6 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java
@@ -24,7 +24,8 @@ import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
/**
- * An {@link IViewRule} for android.widget.ListView and all its derived classes.
+ * An {@link IViewRule} for android.widget.ListView and all its derived classes such
+ * as ExpandableListView.
* This is the "root" rule, that is used whenever there is not more specific
* rule to apply.
*/
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java
index 301385a..eb1cf47 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java
@@ -34,7 +34,7 @@ public class MapViewRule extends BaseViewRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ if (insertType.isCreate()) {
node.setAttribute(ANDROID_URI, "android:apiKey", //$NON-NLS-1$
"Your API key: see " + //$NON-NLS-1$
"http://code.google.com/android/add-ons/google-apis/mapkey.html"); //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertySettingNodeHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertySettingNodeHandler.java
new file mode 100644
index 0000000..8c57da8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertySettingNodeHandler.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.common.layout;
+
+import com.android.ide.common.api.INode;
+import com.android.ide.common.api.INodeHandler;
+
+/**
+ * A convenience implementation of {@link INodeHandler} for setting a given attribute to a
+ * given value on a particular node.
+ */
+class PropertySettingNodeHandler implements INodeHandler {
+ private final String mNamespaceUri;
+ private final String mAttribute;
+ private final String mValue;
+
+ PropertySettingNodeHandler(String namespaceUri, String attribute, String value) {
+ super();
+ mNamespaceUri = namespaceUri;
+ mAttribute = attribute;
+ mValue = value;
+ }
+
+ public void handle(INode node) {
+ node.setAttribute(mNamespaceUri, mAttribute, mValue);
+ }
+} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RadioGroupRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RadioGroupRule.java
index 039b495..88cce52 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RadioGroupRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RadioGroupRule.java
@@ -33,7 +33,7 @@ public class RadioGroupRule extends LinearLayoutRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ if (insertType.isCreate()) {
for (int i = 0; i < 3; i++) {
INode handle = node.appendChild(LayoutConstants.FQCN_RADIO_BUTTON);
handle.setAttribute(ANDROID_URI, ATTR_ID, String.format("@+id/radio%d", i));
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java
index e3c349a..385dcc5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java
@@ -21,9 +21,15 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
import static com.android.ide.common.layout.LayoutConstants.FQCN_LINEAR_LAYOUT;
+import com.android.ide.common.api.DrawingStyle;
+import com.android.ide.common.api.DropFeedback;
+import com.android.ide.common.api.IDragElement;
+import com.android.ide.common.api.IGraphics;
import com.android.ide.common.api.INode;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
+import com.android.ide.common.api.Point;
+import com.android.ide.common.api.Rect;
/**
* An {@link IViewRule} for android.widget.ScrollView.
@@ -44,7 +50,7 @@ public class ScrollViewRule extends FrameLayoutRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ if (insertType.isCreate()) {
// Insert a default linear layout (which will in turn be registered as
// a child of this node and the create child method above will set its
// fill parent attributes, its id, etc.
@@ -52,4 +58,32 @@ public class ScrollViewRule extends FrameLayoutRule {
}
}
+ @Override
+ public DropFeedback onDropMove(INode targetNode, IDragElement[] elements,
+ DropFeedback feedback, Point p) {
+ DropFeedback f = super.onDropMove(targetNode, elements, feedback, p);
+
+ // ScrollViews only allow a single child
+ if (targetNode.getChildren().length > 0) {
+ f.invalidTarget = true;
+ }
+ return f;
+ }
+
+ @Override
+ protected void drawFeedback(
+ IGraphics gc,
+ INode targetNode,
+ IDragElement[] elements,
+ DropFeedback feedback) {
+ if (targetNode.getChildren().length > 0) {
+ Rect b = targetNode.getBounds();
+ if (b.isValid()) {
+ gc.useStyle(DrawingStyle.DROP_RECIPIENT);
+ gc.drawRect(b);
+ }
+ } else {
+ super.drawFeedback(gc, targetNode, elements, feedback);
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SlidingDrawerRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SlidingDrawerRule.java
index 15c3b4c..4af0ae9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SlidingDrawerRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SlidingDrawerRule.java
@@ -37,7 +37,7 @@ public class SlidingDrawerRule extends BaseLayoutRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ if (insertType.isCreate()) {
String matchParent = getFillParentValueName();
node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, matchParent);
node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, matchParent);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java
index 92decde..2d7625b 100755..100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java
@@ -43,7 +43,7 @@ public class TabHostRule extends IgnoredLayoutRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ if (insertType.isCreate()) {
String fillParent = getFillParentValueName();
// Configure default Table setup as described in the Table tutorial
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java
index 6ac670f..cc67d3a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java
@@ -17,13 +17,19 @@ package com.android.ide.common.layout;
import static com.android.ide.common.layout.LayoutConstants.FQCN_TABLE_ROW;
+import com.android.ide.common.api.IClientRulesEngine;
import com.android.ide.common.api.IMenuCallback;
import com.android.ide.common.api.INode;
+import com.android.ide.common.api.INodeHandler;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
import com.android.ide.common.api.MenuAction;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* An {@link IViewRule} for android.widget.TableLayout.
@@ -33,6 +39,13 @@ public class TableLayoutRule extends LinearLayoutRule {
// the default is vertical, not horizontal
// The fill of all children should be wrap_content
+ private static final String ACTION_ADD_ROW = "_addrow"; //$NON-NLS-1$
+ private static final String ACTION_REMOVE_ROW = "_removerow"; //$NON-NLS-1$
+ private static final URL ICON_ADD_ROW =
+ TableLayoutRule.class.getResource("addrow.png"); //$NON-NLS-1$
+ private static final URL ICON_REMOVE_ROW =
+ TableLayoutRule.class.getResource("removerow.png"); //$NON-NLS-1$
+
@Override
protected boolean isVertical(INode node) {
// Tables are always vertical
@@ -58,8 +71,8 @@ public class TableLayoutRule extends LinearLayoutRule {
IMenuCallback addTab = new IMenuCallback() {
public void action(MenuAction action, final String valueId, Boolean newValue) {
final INode node = selectedNode;
- node.appendChild(FQCN_TABLE_ROW);
-
+ INode newRow = node.appendChild(FQCN_TABLE_ROW);
+ mRulesEngine.select(Collections.singletonList(newRow));
}
};
return concatenate(super.getContextMenu(selectedNode),
@@ -67,4 +80,99 @@ public class TableLayoutRule extends LinearLayoutRule {
null, addTab));
}
+ @Override
+ public void addLayoutActions(List<MenuAction> actions, final INode parentNode,
+ final List<? extends INode> children) {
+ super.addLayoutActions(actions, parentNode, children);
+ addTableLayoutActions(mRulesEngine, actions, parentNode, children);
+ }
+
+ /**
+ * Adds layout actions to add and remove toolbar items
+ */
+ static void addTableLayoutActions(final IClientRulesEngine rulesEngine,
+ List<MenuAction> actions, final INode parentNode,
+ final List<? extends INode> children) {
+ IMenuCallback actionCallback = new IMenuCallback() {
+ public void action(final MenuAction action, final String valueId,
+ final Boolean newValue) {
+ parentNode.editXml("Add/Remove Table Row", new INodeHandler() {
+ public void handle(INode n) {
+ if (action.getId().equals(ACTION_ADD_ROW)) {
+ // Determine the index of the selection, if any; if there is
+ // a selection, insert the row before the current row, otherwise
+ // append it to the table.
+ int index = -1;
+ INode[] rows = parentNode.getChildren();
+ if (children != null) {
+ findTableIndex:
+ for (INode child : children) {
+ // Find direct child of table layout
+ while (child != null && child.getParent() != parentNode) {
+ child = child.getParent();
+ }
+ if (child != null) {
+ // Compute index of direct child of table layout
+ for (int i = 0; i < rows.length; i++) {
+ if (rows[i] == child) {
+ index = i;
+ break findTableIndex;
+ }
+ }
+ }
+ }
+ }
+ INode newRow;
+ if (index == -1) {
+ newRow = parentNode.appendChild(FQCN_TABLE_ROW);
+ } else {
+ newRow = parentNode.insertChildAt(FQCN_TABLE_ROW, index);
+ }
+ rulesEngine.select(Collections.singletonList(newRow));
+ } else if (action.getId().equals(ACTION_REMOVE_ROW)) {
+ // Find the direct children of the TableLayout to delete;
+ // this is necessary since TableRow might also use
+ // this implementation, so the parentNode is the true
+ // TableLayout but the children might be grand children.
+ Set<INode> targets = new HashSet<INode>();
+ for (INode child : children) {
+ while (child != null && child.getParent() != parentNode) {
+ child = child.getParent();
+ }
+ if (child != null) {
+ targets.add(child);
+ }
+ }
+ for (INode target : targets) {
+ parentNode.removeChild(target);
+ }
+ }
+ }
+ });
+ }
+ };
+
+ // Add Row
+ actions.add(MenuAction.createSeparator(150));
+ actions.add(MenuAction.createAction(ACTION_ADD_ROW, "Add Table Row", null, actionCallback,
+ ICON_ADD_ROW, 160));
+
+ // Remove Row (if something is selected)
+ if (children != null && children.size() > 0) {
+ actions.add(MenuAction.createAction(ACTION_REMOVE_ROW, "Remove Table Row", null,
+ actionCallback, ICON_REMOVE_ROW, 170));
+ }
+ }
+
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ if (insertType.isCreate()) {
+ // Start the table with 4 rows
+ for (int i = 0; i < 4; i++) {
+ node.appendChild(FQCN_TABLE_ROW);
+ }
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java
index e734f4a..ac03653 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java
@@ -15,9 +15,14 @@
*/
package com.android.ide.common.layout;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_TABLE_LAYOUT;
+
import com.android.ide.common.api.INode;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
+import com.android.ide.common.api.MenuAction;
+
+import java.util.List;
/**
* An {@link IViewRule} for android.widget.TableRow.
@@ -40,4 +45,20 @@ public class TableRowRule extends LinearLayoutRule {
// respectively.
}
+ @Override
+ public void addLayoutActions(List<MenuAction> actions, final INode parentNode,
+ final List<? extends INode> children) {
+ super.addLayoutActions(actions, parentNode, children);
+
+ // Also apply table-specific actions on the table row such that you can
+ // select something in a table row and still get offered actions on the surrounding
+ // table.
+ if (children != null) {
+ INode grandParent = parentNode.getParent();
+ if (grandParent != null && grandParent.getFqcn().equals(FQCN_TABLE_LAYOUT)) {
+ TableLayoutRule.addTableLayoutActions(mRulesEngine, actions, grandParent,
+ children);
+ }
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/WebViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/WebViewRule.java
index 8ec53db..00085c8 100755..100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/WebViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/WebViewRule.java
@@ -35,7 +35,7 @@ public class WebViewRule extends IgnoredLayoutRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ if (insertType.isCreate()) {
String matchParent = getFillParentValueName();
node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, matchParent);
node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, matchParent);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomButtonRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomButtonRule.java
index ca0413e..1200da9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomButtonRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomButtonRule.java
@@ -26,7 +26,7 @@ public class ZoomButtonRule extends BaseViewRule {
public void onCreate(INode node, INode parent, InsertType insertType) {
super.onCreate(node, parent, insertType);
- if (insertType == InsertType.CREATE) {
+ if (insertType.isCreate()) {
node.setAttribute(ANDROID_URI, ATTR_SRC, "@android:drawable/btn_plus"); //$NON-NLS-1$
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addrow.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addrow.png
new file mode 100644
index 0000000..0faa3e6
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addrow.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removerow.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removerow.png
new file mode 100644
index 0000000..db695a7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removerow.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttrsXmlParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttrsXmlParser.java
index bd79e29..1e14f9f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttrsXmlParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttrsXmlParser.java
@@ -256,7 +256,7 @@ public final class AttrsXmlParser {
if (parents != null) {
String[] parentsArray =
parseStyleableParents(parents, mStyleMap, unknownParents);
- style.setParents(parentsArray); //$NON-NLS-1$
+ style.setParents(parentsArray);
}
mStyleMap.put(name, style);
unknownParents.remove(name);
@@ -441,9 +441,9 @@ public final class AttrsXmlParser {
* </ul>
* The format may be one type or two types (e.g. "reference|color").
* An extra format can be implied: "enum" or "flag" are not specified in the "format" attribute,
- * they are implicitely stated by the presence of sub-nodes <enum> or <flag>.
+ * they are implicitly stated by the presence of sub-nodes <enum> or <flag>.
* <p/>
- * By design, <attr> nodes of the same name MUST have the same type.
+ * By design, attr nodes of the same name MUST have the same type.
* Attribute nodes are thus cached by name and reused as much as possible.
* When reusing a node, it is duplicated and its javadoc reassigned.
*/
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
index 85e265c..e7c5157 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt;
+import com.android.AndroidConstants;
import com.android.ide.eclipse.adt.internal.build.builders.PostCompilerBuilder;
import com.android.ide.eclipse.adt.internal.build.builders.PreCompilerBuilder;
import com.android.ide.eclipse.adt.internal.build.builders.ResourceManagerBuilder;
@@ -43,7 +44,7 @@ import java.util.regex.Pattern;
* </ul>
*
*/
-public class AndroidConstants {
+public class AdtConstants {
/**
* The old Editors Plugin ID. It is still used in some places for compatibility.
* Please do not use for new features.
@@ -144,7 +145,7 @@ public class AndroidConstants {
public final static String WS_ASSETS = WS_SEP + SdkConstants.FD_ASSETS;
/** Absolute path of the layout folder, e.g. "/res/layout".<br> This is a workspace path. */
- public final static String WS_LAYOUTS = WS_RESOURCES + WS_SEP + SdkConstants.FD_LAYOUT;
+ public final static String WS_LAYOUTS = WS_RESOURCES + WS_SEP + AndroidConstants.FD_RES_LAYOUT;
/** Leaf of the javaDoc folder. Does not start with a separator. */
public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/" + //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index d7f2e3c..55a5ef8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -19,9 +19,15 @@ package com.android.ide.eclipse.adt;
import com.android.ddmuilib.console.DdmConsole;
import com.android.ddmuilib.console.IDdmConsole;
import com.android.ide.common.log.ILogger;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceFolder;
import com.android.ide.common.sdk.LoadStatus;
import com.android.ide.eclipse.adt.internal.VersionCheck;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.animator.AnimationEditor;
+import com.android.ide.eclipse.adt.internal.editors.color.ColorEditor;
+import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder;
import com.android.ide.eclipse.adt.internal.editors.menu.MenuEditor;
@@ -34,18 +40,16 @@ import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
import com.android.ide.eclipse.adt.internal.ui.EclipseUiHelper;
import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.io.StreamException;
+import com.android.resources.ResourceFolderType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.StreamException;
import com.android.sdkstats.SdkStatsService;
import org.eclipse.core.resources.IFile;
@@ -64,10 +68,14 @@ import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.graphics.Color;
@@ -79,7 +87,10 @@ import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.browser.IWebBrowser;
+import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleConstants;
@@ -114,6 +125,15 @@ import java.util.List;
* The activator class controls the plug-in life cycle
*/
public class AdtPlugin extends AbstractUIPlugin implements ILogger {
+ /**
+ * Temporary logging code to help track down
+ * http://code.google.com/p/android/issues/detail?id=15003
+ *
+ * Deactivated right now.
+ * TODO remove this and associated logging code once we're done with issue 15003.
+ */
+ public static final boolean DEBUG_XML_FILE_INIT = false;
+
/** The plug-in ID */
public static final String PLUGIN_ID = "com.android.ide.eclipse.adt"; //$NON-NLS-1$
@@ -354,7 +374,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
/** Returns the absolute traceview path */
public static String getOsAbsoluteTraceview() {
return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER +
- AndroidConstants.FN_TRACEVIEW;
+ AdtConstants.FN_TRACEVIEW;
}
/** Returns the absolute emulator path */
@@ -364,7 +384,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
public static String getOsAbsoluteHprofConv() {
return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER +
- AndroidConstants.FN_HPROF_CONV;
+ AdtConstants.FN_HPROF_CONV;
}
/** Returns the absolute proguard path */
@@ -377,7 +397,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
*/
public static String getUrlDoc() {
return ProjectHelper.getJavaDocPath(
- getOsSdkFolder() + AndroidConstants.WS_JAVADOC_FOLDER_LEAF);
+ getOsSdkFolder() + AdtConstants.WS_JAVADOC_FOLDER_LEAF);
}
/**
@@ -778,7 +798,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
public static InputStream readEmbeddedFileAsStream(String filepath) {
// attempt to read an embedded file
try {
- URL url = getEmbeddedFileUrl(AndroidConstants.WS_SEP + filepath);
+ URL url = getEmbeddedFileUrl(AdtConstants.WS_SEP + filepath);
if (url != null) {
return url.openStream();
}
@@ -811,8 +831,8 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
// attempt to get a file to one of the template.
String path = filepath;
- if (!path.startsWith(AndroidConstants.WS_SEP)) {
- path = AndroidConstants.WS_SEP + path;
+ if (!path.startsWith(AdtConstants.WS_SEP)) {
+ path = AdtConstants.WS_SEP + path;
}
URL url = bundle.getEntry(path);
@@ -1148,7 +1168,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
// not meant to be exhaustive.
String[] filesToCheck = new String[] {
osSdkLocation + getOsRelativeAdb(),
- osSdkLocation + getOsRelativeEmulator()
+ osSdkLocation + getOsRelativeEmulator() + SdkConstants.FN_EMULATOR_EXTENSION
};
for (String file : filesToCheck) {
if (checkFile(file) == false) {
@@ -1416,10 +1436,15 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
* @see IFileListener#fileChanged
*/
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
- if (AndroidConstants.EXT_XML.equals(file.getFileExtension())) {
+ if (AdtConstants.EXT_XML.equals(file.getFileExtension())) {
// The resources files must have a file path similar to
// project/res/.../*.xml
// There is no support for sub folders, so the segment count must be 4
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, "fileChanged %1$s",
+ file.getFullPath().toOSString());
+ }
+
if (file.getFullPath().segmentCount() == 4) {
// check if we are inside the res folder.
String segment = file.getFullPath().segment(1);
@@ -1449,34 +1474,67 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
resourceChanged(file, resFolder.getType());
}
} else {
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " The resource folder was null");
+ }
+
// if the res folder is null, this means the name is invalid,
// in this case we remove whatever android editors that was set
// as the default editor.
IEditorDescriptor desc = IDE.getDefaultEditor(file);
String editorId = desc.getId();
- if (editorId.startsWith(AndroidConstants.EDITORS_NAMESPACE)) {
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, "Old editor id=%1$s", editorId);
+ }
+ if (editorId.startsWith(AdtConstants.EDITORS_NAMESPACE)) {
// reset the default editor.
IDE.setDefaultEditor(file, null);
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " Resetting editor id to default");
+ }
}
}
+ } else {
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " Not in resources/, ignoring");
+ }
}
}
}
}
private void resourceAdded(IFile file, ResourceFolderType type) {
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, "resourceAdded %1$s - type=%1$s",
+ file.getFullPath().toOSString(), type);
+ }
// set the default editor based on the type.
if (type == ResourceFolderType.LAYOUT) {
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " set default editor id to layout");
+ }
IDE.setDefaultEditor(file, LayoutEditor.ID);
- } else if (type == ResourceFolderType.DRAWABLE
- || type == ResourceFolderType.VALUES) {
+ } else if (type == ResourceFolderType.VALUES) {
IDE.setDefaultEditor(file, ResourcesEditor.ID);
} else if (type == ResourceFolderType.MENU) {
IDE.setDefaultEditor(file, MenuEditor.ID);
+ } else if (type == ResourceFolderType.COLOR) {
+ IDE.setDefaultEditor(file, ColorEditor.ID);
+ } else if (type == ResourceFolderType.DRAWABLE) {
+ IDE.setDefaultEditor(file, DrawableEditor.ID);
+ } else if (type == ResourceFolderType.ANIMATOR
+ || type == ResourceFolderType.ANIM) {
+ IDE.setDefaultEditor(file, AnimationEditor.ID);
} else if (type == ResourceFolderType.XML) {
if (XmlEditor.canHandleFile(file)) {
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " set default editor id to XmlEditor.id");
+ }
IDE.setDefaultEditor(file, XmlEditor.ID);
} else {
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " set default editor id unknown");
+ }
// set a property to determine later if the XML can be handled
QualifiedName qname = new QualifiedName(
AdtPlugin.PLUGIN_ID,
@@ -1487,6 +1545,10 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
}
private void resourceChanged(IFile file, ResourceFolderType type) {
+ if (DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, "resourceChanged %1$s - type=%1$s",
+ file.getFullPath().toOSString(), type);
+ }
if (type == ResourceFolderType.XML) {
IEditorDescriptor ed = IDE.getDefaultEditor(file);
if (ed == null || ed.getId() != XmlEditor.ID) {
@@ -1707,4 +1769,99 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
public void warning(String format, Object... args) {
log(IStatus.WARNING, format, args);
}
+
+ /**
+ * Opens the given URL in a browser tab
+ *
+ * @param url the URL to open in a browser
+ */
+ public static void openUrl(URL url) {
+ IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport();
+ IWebBrowser browser;
+ try {
+ browser = support.createBrowser(PLUGIN_ID);
+ browser.openURL(url);
+ } catch (PartInitException e) {
+ log(e, null);
+ }
+ }
+
+ /**
+ * Opens a Java class for the given fully qualified class name
+ *
+ * @param project the project containing the class
+ * @param fqcn the fully qualified class name of the class to be opened
+ * @return true if the class was opened, false otherwise
+ */
+ public static boolean openJavaClass(IProject project, String fqcn) {
+ if (fqcn == null) {
+ return false;
+ }
+
+ // Handle inner classes
+ if (fqcn.indexOf('$') != -1) {
+ fqcn = fqcn.replaceAll("\\$", "."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ try {
+ if (project.hasNature(JavaCore.NATURE_ID)) {
+ IJavaProject javaProject = JavaCore.create(project);
+ IJavaElement result = javaProject.findType(fqcn);
+ if (result != null) {
+ return JavaUI.openInEditor(result) != null;
+ }
+ }
+ } catch (Throwable e) {
+ log(e, "Can't open class %1$s", fqcn); //$NON-NLS-1$
+ }
+
+ return false;
+ }
+
+ /**
+ * Opens the given file and shows the given (optional) region in the editor (or
+ * if no region is specified, opens the editor tab.)
+ *
+ * @param file the file to be opened
+ * @param region an optional region which if set will be selected and shown to the
+ * user
+ * @throws PartInitException if something goes wrong
+ */
+ public static void openFile(IFile file, IRegion region) throws PartInitException {
+ openFile(file, region, true);
+ }
+
+ /**
+ * Opens the given file and shows the given (optional) region
+ *
+ * @param file the file to be opened
+ * @param region an optional region which if set will be selected and shown to the
+ * user
+ * @param showEditorTab if true, front the editor tab after opening the file
+ * @throws PartInitException if something goes wrong
+ */
+ public static void openFile(IFile file, IRegion region, boolean showEditorTab)
+ throws PartInitException {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ if (workbench == null) {
+ return;
+ }
+ IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();
+ if (activeWorkbenchWindow == null) {
+ return;
+ }
+ IWorkbenchPage page = activeWorkbenchWindow.getActivePage();
+ if (page == null) {
+ return;
+ }
+ IEditorPart targetEditor = IDE.openEditor(page, file, true);
+ if (targetEditor instanceof AndroidXmlEditor) {
+ AndroidXmlEditor editor = (AndroidXmlEditor) targetEditor;
+ if (region != null) {
+ editor.show(region.getOffset(), region.getLength());
+ } else if (showEditorTab) {
+ editor.setActivePage(AndroidXmlEditor.TEXT_EDITOR_ID);
+ }
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/ConvertToAndroidAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/ConvertToAndroidAction.java
index 92604fb..cf46192 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/ConvertToAndroidAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/ConvertToAndroidAction.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.actions;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import org.eclipse.core.resources.IProject;
@@ -107,7 +107,7 @@ public class ConvertToAndroidAction implements IObjectActionDelegate {
// check if the project already has the android nature.
for (int i = 0; i < natures.length; ++i) {
- if (AndroidConstants.NATURE_DEFAULT.equals(natures[i])) {
+ if (AdtConstants.NATURE_DEFAULT.equals(natures[i])) {
// we shouldn't be here as the visibility of the item
// is dependent on the project.
return new Status(Status.WARNING, AdtPlugin.PLUGIN_ID,
@@ -121,7 +121,7 @@ public class ConvertToAndroidAction implements IObjectActionDelegate {
String[] newNatures = new String[natures.length + 1];
System.arraycopy(natures, 0, newNatures, 1, natures.length);
- newNatures[0] = AndroidConstants.NATURE_DEFAULT;
+ newNatures[0] = AdtConstants.NATURE_DEFAULT;
// set the new nature list in the project
description.setNatureIds(newNatures);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java
index 504cf0b..2a2f0d0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java
@@ -16,13 +16,21 @@
package com.android.ide.eclipse.adt.internal.build;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
+import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.text.FindReplaceDocumentAdapter;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.ui.editors.text.TextFileDocumentProvider;
+import org.eclipse.ui.texteditor.IDocumentProvider;
import java.io.File;
import java.util.List;
@@ -118,6 +126,56 @@ public final class AaptParser {
"^(invalid resource directory name): (.*)$"); //$NON-NLS-1$
/**
+ * Portion of the error message which states the context in which the error occurred,
+ * such as which property was being processed and what the string value was that
+ * caused the error.
+ * <p>
+ * Example:
+ * error: No resource found that matches the given name (at 'text' with value '@string/foo')
+ */
+ private static final Pattern sValueRangePattern =
+ Pattern.compile("\\(at '(.+)' with value '(.*)'\\)"); //$NON-NLS-1$
+
+
+ /**
+ * Portion of error message which points to the second occurrence of a repeated resource
+ * definition.
+ * <p>
+ * Example:
+ * error: Resource entry repeatedStyle1 already has bag item android:gravity.
+ */
+ private static final Pattern sRepeatedRangePattern =
+ Pattern.compile("Resource entry (.+) already has bag item (.+)\\."); //$NON-NLS-1$
+
+ /**
+ * Suffix of error message which points to the first occurrence of a repeated resource
+ * definition.
+ * Example:
+ * Originally defined here.
+ */
+ private static final String ORIGINALLY_DEFINED_MSG = "Originally defined here."; //$NON-NLS-1$
+
+ /**
+ * Portion of error message which points to the second occurrence of a repeated resource
+ * definition.
+ * <p>
+ * Example:
+ * error: Resource entry repeatedStyle1 already has bag item android:gravity.
+ */
+ private static final Pattern sNoResourcePattern =
+ Pattern.compile("No resource found that matches the given name: attr '(.+)'\\."); //$NON-NLS-1$
+
+ /**
+ * Portion of error message which points to a missing required attribute in a
+ * resource definition.
+ * <p>
+ * Example:
+ * error: error: A 'name' attribute is required for <style>
+ */
+ private static final Pattern sRequiredPattern =
+ Pattern.compile("A '(.+)' attribute is required for <(.+)>"); //$NON-NLS-1$
+
+ /**
* 2 line aapt error<br>
* "ERROR: Invalid configuration: foo"<br>
* " ^^^"<br>
@@ -180,7 +238,7 @@ public final class AaptParser {
// check the values and attempt to mark the file.
if (checkAndMark(location, lineStr, msg, osRoot, project,
- AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
+ AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
continue;
@@ -204,7 +262,7 @@ public final class AaptParser {
// display the error
if (checkAndMark(location, null, msg, osRoot, project,
- AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
+ AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -228,7 +286,7 @@ public final class AaptParser {
// check the values and attempt to mark the file.
if (checkAndMark(location, lineStr, msg, osRoot, project,
- AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
+ AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
continue;
@@ -242,7 +300,7 @@ public final class AaptParser {
// check the values and attempt to mark the file.
if (checkAndMark(location, lineStr, msg, osRoot, project,
- AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
+ AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -266,7 +324,7 @@ public final class AaptParser {
// check the values and attempt to mark the file.
if (checkAndMark(location, lineStr, msg, osRoot, project,
- AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
+ AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -282,7 +340,7 @@ public final class AaptParser {
// check the values and attempt to mark the file.
if (checkAndMark(location, lineStr, msg, osRoot, project,
- AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) {
+ AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) {
return true;
}
@@ -298,7 +356,7 @@ public final class AaptParser {
// check the values and attempt to mark the file.
if (checkAndMark(location, lineStr, msg, osRoot, project,
- AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
+ AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -313,7 +371,7 @@ public final class AaptParser {
// check the values and attempt to mark the file.
if (checkAndMark(location, null, msg, osRoot, project,
- AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
+ AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -331,7 +389,7 @@ public final class AaptParser {
// check the values and attempt to mark the file.
if (checkAndMark(null /*location*/, null, msg, osRoot, project,
- AndroidConstants.MARKER_AAPT_PACKAGE, IMarker.SEVERITY_ERROR) == false) {
+ AdtConstants.MARKER_AAPT_PACKAGE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -392,12 +450,36 @@ public final class AaptParser {
}
}
+ // Attempt to determine the exact range of characters affected by this error.
+ // This will look up the actual text of the file, go to the particular error line
+ // and scan for the specific string mentioned in the error.
+ int startOffset = -1;
+ int endOffset = -1;
+ if (f2 instanceof IFile) {
+ IRegion region = findRange((IFile) f2, line, message);
+ if (region != null) {
+ startOffset = region.getOffset();
+ endOffset = startOffset + region.getLength();
+ }
+ }
+
// check if there's a similar marker already, since aapt is launched twice
boolean markerAlreadyExists = false;
try {
IMarker[] markers = f2.findMarkers(markerId, true, IResource.DEPTH_ZERO);
for (IMarker marker : markers) {
+ if (startOffset != -1) {
+ int tmpBegin = marker.getAttribute(IMarker.CHAR_START, -1);
+ if (tmpBegin != startOffset) {
+ break;
+ }
+ int tmpEnd = marker.getAttribute(IMarker.CHAR_END, -1);
+ if (tmpEnd != startOffset) {
+ break;
+ }
+ }
+
int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1);
if (tmpLine != line) {
break;
@@ -425,13 +507,159 @@ public final class AaptParser {
}
if (markerAlreadyExists == false) {
- BaseProjectHelper.markResource(f2, markerId, message, line, severity);
+ BaseProjectHelper.markResource(f2, markerId, message, line,
+ startOffset, endOffset, severity);
}
return true;
}
/**
+ * Given an aapt error message in a given file and a given (initial) line number,
+ * return the corresponding offset range for the error, or null.
+ */
+ private static IRegion findRange(IFile file, int line, String message) {
+ Matcher matcher = sValueRangePattern.matcher(message);
+ if (matcher.find()) {
+ String property = matcher.group(1);
+ String value = matcher.group(2);
+
+ // First find the property. We can't just immediately look for the
+ // value, because there could be other attributes in this element
+ // earlier than the one in error, and we might accidentally pick
+ // up on a different occurrence of the value in a context where
+ // it is valid.
+ if (value.length() > 0) {
+ return findRange(file, line, property, value);
+ } else {
+ // Find first occurrence of property followed by '' or ""
+ IRegion region1 = findRange(file, line, property, "\"\""); //$NON-NLS-1$
+ IRegion region2 = findRange(file, line, property, "''"); //$NON-NLS-1$
+ if (region1 == null) {
+ if (region2 == null) {
+ // Highlight the property instead
+ return findRange(file, line, property, null);
+ }
+ return region2;
+ } else if (region2 == null) {
+ return region1;
+ } else if (region1.getOffset() < region2.getOffset()) {
+ return region1;
+ } else {
+ return region2;
+ }
+ }
+ }
+
+ matcher = sRepeatedRangePattern.matcher(message);
+ if (matcher.find()) {
+ String property = matcher.group(2);
+ return findRange(file, line, property, null);
+ }
+
+ matcher = sNoResourcePattern.matcher(message);
+ if (matcher.find()) {
+ String property = matcher.group(1);
+ return findRange(file, line, property, null);
+ }
+
+ matcher = sRequiredPattern.matcher(message);
+ if (matcher.find()) {
+ String elementName = matcher.group(2);
+ IRegion region = findRange(file, line, '<' + elementName, null);
+ if (region != null && region.getLength() > 1) {
+ // Skip the opening <
+ region = new Region(region.getOffset() + 1, region.getLength() - 1);
+ }
+ return region;
+ }
+
+ if (message.endsWith(ORIGINALLY_DEFINED_MSG)) {
+ return findLineTextRange(file, line);
+ }
+
+ return null;
+ }
+
+ /**
+ * Given a file and line number, return the range of the first match starting on the
+ * given line. If second is non null, also search for the second string starting at he
+ * location of the first string.
+ */
+ private static IRegion findRange(IFile file, int line, String first,
+ String second) {
+ IRegion region = null;
+ IDocumentProvider provider = new TextFileDocumentProvider();
+ try {
+ provider.connect(file);
+ IDocument document = provider.getDocument(file);
+ if (document != null) {
+ IRegion lineInfo = document.getLineInformation(line - 1);
+ int lineStartOffset = lineInfo.getOffset();
+ // The aapt errors will be anchored on the line where the
+ // element starts - which means that with formatting where
+ // attributes end up on subsequent lines we don't find it on
+ // the error line indicated by aapt.
+ // Therefore, search forwards in the document.
+ FindReplaceDocumentAdapter adapter =
+ new FindReplaceDocumentAdapter(document);
+
+ region = adapter.find(lineStartOffset, first,
+ true /*forwardSearch*/, true /*caseSensitive*/,
+ false /*wholeWord*/, false /*regExSearch*/);
+ if (region != null && second != null) {
+ region = adapter.find(region.getOffset() + first.length(), second,
+ true /*forwardSearch*/, true /*caseSensitive*/,
+ false /*wholeWord*/, false /*regExSearch*/);
+ }
+ }
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Can't find range information for %1$s", file.getName());
+ } finally {
+ provider.disconnect(file);
+ }
+ return region;
+ }
+
+ /** Returns the non-whitespace line range at the given line number. */
+ private static IRegion findLineTextRange(IFile file, int line) {
+ IDocumentProvider provider = new TextFileDocumentProvider();
+ try {
+ provider.connect(file);
+ IDocument document = provider.getDocument(file);
+ if (document != null) {
+ IRegion lineInfo = document.getLineInformation(line - 1);
+ String lineContents = document.get(lineInfo.getOffset(), lineInfo.getLength());
+ int lineBegin = 0;
+ int lineEnd = lineContents.length()-1;
+
+ for (; lineEnd >= 0; lineEnd--) {
+ char c = lineContents.charAt(lineEnd);
+ if (!Character.isWhitespace(c)) {
+ break;
+ }
+ }
+ lineEnd++;
+ for (; lineBegin < lineEnd; lineBegin++) {
+ char c = lineContents.charAt(lineBegin);
+ if (!Character.isWhitespace(c)) {
+ break;
+ }
+ }
+ if (lineBegin < lineEnd) {
+ return new Region(lineInfo.getOffset() + lineBegin, lineEnd - lineBegin);
+ }
+ }
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Can't find range information for %1$s", file.getName());
+ } finally {
+ provider.disconnect(file);
+ }
+
+ return null;
+ }
+
+ /**
* Returns a matching matcher for the next line
* @param lines The array of lines
* @param nextIndex The index of the next line
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFix.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFix.java
new file mode 100644
index 0000000..7d79086
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFix.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2011 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.build;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
+import com.android.resources.ResourceType;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.IMarkerResolution2;
+import org.eclipse.ui.IMarkerResolutionGenerator2;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.editors.text.TextFileDocumentProvider;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Shared handler for both quick assist processors (Control key handler) and quick fix
+ * marker resolution (Problem view handling), since there is a lot of overlap between
+ * these two UI handlers.
+ */
+@SuppressWarnings("restriction") // XML model
+public class AaptQuickFix implements IMarkerResolutionGenerator2, IQuickAssistProcessor {
+
+ public AaptQuickFix() {
+ }
+
+ /** Returns the error message from aapt that signals missing resources */
+ private static String getTargetMarkerErrorMessage() {
+ return "No resource found that matches the given name";
+ }
+
+ /** Returns the error message from aapt that signals a missing namespace declaration */
+ private static String getUnboundErrorMessage() {
+ return "Error parsing XML: unbound prefix";
+ }
+
+ // ---- Implements IMarkerResolution2 ----
+
+ public boolean hasResolutions(IMarker marker) {
+ String message = null;
+ try {
+ message = (String) marker.getAttribute(IMarker.MESSAGE);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+
+ return message != null
+ && (message.contains(getTargetMarkerErrorMessage())
+ || message.contains(getUnboundErrorMessage()));
+ }
+
+ public IMarkerResolution[] getResolutions(IMarker marker) {
+ IResource markerResource = marker.getResource();
+ IProject project = markerResource.getProject();
+ try {
+ String message = (String) marker.getAttribute(IMarker.MESSAGE);
+ if (message.contains(getUnboundErrorMessage()) && markerResource instanceof IFile) {
+ return new IMarkerResolution[] {
+ new CreateNamespaceFix((IFile) markerResource)
+ };
+ }
+ } catch (CoreException e1) {
+ AdtPlugin.log(e1, null);
+ }
+
+ int start = marker.getAttribute(IMarker.CHAR_START, 0);
+ int end = marker.getAttribute(IMarker.CHAR_END, 0);
+ if (end > start) {
+ int length = end - start;
+ IDocumentProvider provider = new TextFileDocumentProvider();
+ try {
+ provider.connect(markerResource);
+ IDocument document = provider.getDocument(markerResource);
+ String resource = document.get(start, length);
+ if (ResourceHelper.canCreateResource(resource)) {
+ return new IMarkerResolution[] {
+ new CreateResourceProposal(project, resource)
+ };
+ }
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Can't find range information for %1$s", markerResource);
+ } finally {
+ provider.disconnect(markerResource);
+ }
+ }
+
+ return null;
+ }
+
+ // ---- Implements IQuickAssistProcessor ----
+
+ public boolean canAssist(IQuickAssistInvocationContext invocationContext) {
+ return true;
+ }
+
+ public boolean canFix(Annotation annotation) {
+ return true;
+ }
+
+ public ICompletionProposal[] computeQuickAssistProposals(
+ IQuickAssistInvocationContext invocationContext) {
+
+ // We have to find the corresponding project/file (so we can look up the aapt
+ // error markers). Unfortunately, an IQuickAssistProcessor only gets
+ // access to an ISourceViewer which has no hooks back to the surrounding
+ // editor.
+ //
+ // However, the IQuickAssistProcessor will only be used interactively by a file
+ // being edited, so we can cheat like the hyperlink detector and simply
+ // look up the currently active file in the IDE. To be on the safe side,
+ // we'll make sure that that editor has the same sourceViewer such that
+ // we are indeed looking at the right file:
+ ISourceViewer sourceViewer = invocationContext.getSourceViewer();
+ AndroidXmlEditor editor = AndroidXmlEditor.getAndroidXmlEditor(sourceViewer);
+ if (editor != null) {
+ IFile file = editor.getInputFile();
+
+ try {
+ IMarker[] markers = file.findMarkers(AdtConstants.MARKER_AAPT_COMPILE, true,
+ IResource.DEPTH_ZERO);
+
+ // Look for a match on the same line as the caret.
+ int offset = invocationContext.getOffset();
+ IDocument document = sourceViewer.getDocument();
+ IRegion lineInfo = document.getLineInformationOfOffset(offset);
+ int lineStart = lineInfo.getOffset();
+ int lineEnd = lineStart + lineInfo.getLength();
+
+ for (IMarker marker : markers) {
+ String message = marker.getAttribute(IMarker.MESSAGE, ""); //$NON-NLS-1$
+ if (message.contains(getTargetMarkerErrorMessage())) {
+ int start = marker.getAttribute(IMarker.CHAR_START, 0);
+ int end = marker.getAttribute(IMarker.CHAR_END, 0);
+ if (start >= lineStart && start <= lineEnd && end > start) {
+ int length = end - start;
+ String resource = document.get(start, length);
+ // Can only offer create value for non-framework value
+ // resources
+ if (ResourceHelper.canCreateResource(resource)) {
+ IProject project = editor.getProject();
+ return new ICompletionProposal[] {
+ new CreateResourceProposal(project, resource)
+ };
+ }
+ }
+ } else if (message.contains(getUnboundErrorMessage())) {
+ return new ICompletionProposal[] {
+ new CreateNamespaceFix(null)
+ };
+ }
+ }
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ } catch (BadLocationException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+
+ return null;
+ }
+
+ public String getErrorMessage() {
+ return null;
+ }
+
+ /** Quick fix to insert namespace binding when missing */
+ private final static class CreateNamespaceFix
+ implements ICompletionProposal, IMarkerResolution2 {
+ private IFile mFile;
+
+ public CreateNamespaceFix(IFile file) {
+ mFile = file;
+ }
+
+ private IndexedRegion perform(IDocument doc) {
+ IModelManager manager = StructuredModelManager.getModelManager();
+ IStructuredModel model = manager.getExistingModelForEdit(doc);
+ if (model != null) {
+ try {
+ perform(model);
+ } finally {
+ model.releaseFromEdit();
+ }
+ }
+
+ return null;
+ }
+
+ private IndexedRegion perform(IFile file) {
+ IModelManager manager = StructuredModelManager.getModelManager();
+ IStructuredModel model;
+ try {
+ model = manager.getModelForEdit(file);
+ if (model != null) {
+ try {
+ perform(model);
+ } finally {
+ model.releaseFromEdit();
+ }
+ }
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Can't look up XML model");
+ }
+
+ return null;
+ }
+
+ private IndexedRegion perform(IStructuredModel model) {
+ if (model instanceof IDOMModel) {
+ IDOMModel domModel = (IDOMModel) model;
+ Document document = domModel.getDocument();
+ Element element = document.getDocumentElement();
+ Attr attr = document.createAttributeNS(XmlnsAttributeDescriptor.XMLNS_URI,
+ "xmlns:android"); //$NON-NLS-1$
+ attr.setValue(ANDROID_URI);
+ element.getAttributes().setNamedItemNS(attr);
+ return (IndexedRegion) attr;
+ }
+
+ return null;
+ }
+
+ // ---- Implements ICompletionProposal ----
+
+ public void apply(IDocument document) {
+ perform(document);
+ }
+
+ public String getAdditionalProposalInfo() {
+ return "Adds an Android namespace declaratiopn to the root element.";
+ }
+
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ public String getDisplayString() {
+ return "Insert namespace binding";
+ }
+
+ public Image getImage() {
+ return AdtPlugin.getAndroidLogo();
+ }
+
+ public Point getSelection(IDocument doc) {
+ return null;
+ }
+
+
+ // ---- Implements MarkerResolution2 ----
+
+ public String getLabel() {
+ return getDisplayString();
+ }
+
+ public void run(IMarker marker) {
+ try {
+ AdtPlugin.openFile(mFile, null);
+ } catch (PartInitException e) {
+ AdtPlugin.log(e, "Can't open file %1$s", mFile.getName());
+ }
+
+ IndexedRegion indexedRegion = perform(mFile);
+ if (indexedRegion != null) {
+ try {
+ IRegion region =
+ new Region(indexedRegion.getStartOffset(), indexedRegion.getLength());
+ AdtPlugin.openFile(mFile, region);
+ } catch (PartInitException e) {
+ AdtPlugin.log(e, "Can't open file %1$s", mFile.getName());
+ }
+ }
+ }
+
+ public String getDescription() {
+ return getAdditionalProposalInfo();
+ }
+ }
+
+ private static class CreateResourceProposal
+ implements ICompletionProposal, IMarkerResolution2 {
+ private final IProject mProject;
+ private final String mResource;
+
+ CreateResourceProposal(IProject project, String resource) {
+ super();
+ mProject = project;
+ mResource = resource;
+ }
+
+ private void perform() {
+ Pair<ResourceType,String> resource = ResourceHelper.parseResource(mResource);
+ ResourceType type = resource.getFirst();
+ String name = resource.getSecond();
+ String value = ""; //$NON-NLS-1$
+
+ // Try to pick a reasonable first guess. The new value will be highlighted and
+ // selected for editing, but if we have an initial value then the new file
+ // won't show an error.
+ switch (type) {
+ case STRING: value = "TODO"; break; //$NON-NLS-1$
+ case DIMEN: value = "1dp"; break; //$NON-NLS-1$
+ case BOOL: value = "true"; break; //$NON-NLS-1$
+ case COLOR: value = "#000000"; break; //$NON-NLS-1$
+ case INTEGER: value = "1"; break; //$NON-NLS-1$
+ case ARRAY: value = "<item>1</item>"; break; //$NON-NLS-1$
+ }
+
+ Pair<IFile, IRegion> location =
+ ResourceHelper.createResource(mProject, type, name, value);
+ if (location != null) {
+ IFile file = location.getFirst();
+ IRegion region = location.getSecond();
+ try {
+ AdtPlugin.openFile(file, region);
+ } catch (PartInitException e) {
+ AdtPlugin.log(e, "Can't open file %1$s", file.getName());
+ }
+ }
+ }
+
+ // ---- Implements ICompletionProposal ----
+
+ public void apply(IDocument document) {
+ perform();
+ }
+
+ public String getAdditionalProposalInfo() {
+ return "Creates an XML file entry for the given missing resource "
+ + "and opens it in the editor.";
+ }
+
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ public String getDisplayString() {
+ return String.format("Create resource %1$s", mResource);
+ }
+
+ public Image getImage() {
+ return AdtPlugin.getAndroidLogo();
+ }
+
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+
+ // ---- Implements MarkerResolution2 ----
+
+ public String getLabel() {
+ return getDisplayString();
+ }
+
+ public void run(IMarker marker) {
+ perform();
+ }
+
+ public String getDescription() {
+ return getAdditionalProposalInfo();
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AidlProcessor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AidlProcessor.java
index 9d6a819..09dd571 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AidlProcessor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AidlProcessor.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.build;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
@@ -81,7 +81,7 @@ public class AidlProcessor extends SourceProcessor {
@Override
protected String getExtension() {
- return AndroidConstants.EXT_AIDL;
+ return AdtConstants.EXT_AIDL;
}
@Override
@@ -105,13 +105,15 @@ public class AidlProcessor extends SourceProcessor {
IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
for (IPath p : sourceFolders) {
IFolder f = wsRoot.getFolder(p);
- command[index++] = quote("-I" + f.getLocation().toOSString()); //$NON-NLS-1$
+ if (f.exists()) { // if the resource doesn't exist, getLocation will return null.
+ command[index++] = quote("-I" + f.getLocation().toOSString()); //$NON-NLS-1$
+ }
}
boolean verbose = AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE;
// remove the generic marker from the project
- builder.removeMarkersFromResource(project, AndroidConstants.MARKER_AIDL);
+ builder.removeMarkersFromResource(project, AdtConstants.MARKER_AIDL);
// loop until we've compile them all
for (IFile sourceFile : sources) {
@@ -127,7 +129,7 @@ public class AidlProcessor extends SourceProcessor {
}
// Remove the AIDL error markers from the aidl file
- builder.removeMarkersFromResource(sourceFile, AndroidConstants.MARKER_AIDL);
+ builder.removeMarkersFromResource(sourceFile, AdtConstants.MARKER_AIDL);
// get the path of the source file.
IPath sourcePath = sourceFile.getLocation();
@@ -222,7 +224,7 @@ public class AidlProcessor extends SourceProcessor {
AdtPlugin.printErrorToConsole(project, results.toArray());
// mark the project
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_AIDL,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_AIDL,
Messages.Unparsed_AIDL_Errors, IMarker.SEVERITY_ERROR);
} else {
AdtPlugin.printToConsole(project, results.toArray());
@@ -233,13 +235,13 @@ public class AidlProcessor extends SourceProcessor {
} catch (IOException e) {
// mark the project and exit
String msg = String.format(Messages.AIDL_Exec_Error, command[0]);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_AIDL, msg,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_AIDL, msg,
IMarker.SEVERITY_ERROR);
return false;
} catch (InterruptedException e) {
// mark the project and exit
String msg = String.format(Messages.AIDL_Exec_Error, command[0]);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_AIDL, msg,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_AIDL, msg,
IMarker.SEVERITY_ERROR);
return false;
}
@@ -282,7 +284,7 @@ public class AidlProcessor extends SourceProcessor {
}
// mark the file
- BaseProjectHelper.markResource(file, AndroidConstants.MARKER_AIDL, msg, line,
+ BaseProjectHelper.markResource(file, AdtConstants.MARKER_AIDL, msg, line,
IMarker.SEVERITY_ERROR);
// success, go to the next line
@@ -329,7 +331,7 @@ public class AidlProcessor extends SourceProcessor {
// Build the Java file name from the aidl name.
String javaName = sourceFile.getName().replaceAll(
- AndroidConstants.RE_AIDL_EXT, AndroidConstants.DOT_JAVA);
+ AdtConstants.RE_AIDL_EXT, AdtConstants.DOT_JAVA);
// get the resource for the java file.
IFile javaFile = destinationFolder.getFile(javaName);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
index 545d0f9..1d6bad3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.build;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AndroidPrintStream;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
@@ -145,10 +145,10 @@ public class BuildHelper {
// need to figure out some path before we can execute aapt;
// get the resource folder
- IFolder resFolder = mProject.getFolder(AndroidConstants.WS_RESOURCES);
+ IFolder resFolder = mProject.getFolder(AdtConstants.WS_RESOURCES);
// and the assets folder
- IFolder assetsFolder = mProject.getFolder(AndroidConstants.WS_ASSETS);
+ IFolder assetsFolder = mProject.getFolder(AdtConstants.WS_ASSETS);
// we need to make sure this one exists.
if (assetsFolder.exists() == false) {
@@ -747,7 +747,7 @@ public class BuildHelper {
// (This is to handle the case of reference Android projects in the context of
// instrumentation projects that need to reference the projects to be tested).
if (referencedJavaProject.getProject().hasNature(
- AndroidConstants.NATURE_DEFAULT) == false) {
+ AdtConstants.NATURE_DEFAULT) == false) {
writeStandardProjectResources(apkBuilder, referencedJavaProject, wsRoot, list);
}
}
@@ -842,7 +842,7 @@ public class BuildHelper {
IResource resource = wsRoot.findMember(path);
// case of a jar file (which could be relative to the workspace or a full path)
- if (AndroidConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
+ if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
if (resource != null && resource.exists() &&
resource.getType() == IResource.FILE) {
oslibraryList.add(resource.getLocation().toOSString());
@@ -908,7 +908,7 @@ public class BuildHelper {
// only include output from non android referenced project
// (This is to handle the case of reference Android projects in the context of
// instrumentation projects that need to reference the projects to be tested).
- if (javaProject.getProject().hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (javaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT) == false) {
// get the output folder
IPath path = null;
try {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java
index 30e3d5c..f13e201 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java
@@ -16,14 +16,15 @@
package com.android.ide.eclipse.adt.internal.build;
+import com.android.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.resources.ResourceFolderType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
@@ -70,7 +71,7 @@ public class RenderScriptProcessor extends SourceProcessor {
boolean r = super.handleGeneratedFile(file, kind);
if (r == false &&
kind == IResourceDelta.REMOVED &&
- AndroidConstants.EXT_DEP.equalsIgnoreCase(file.getFileExtension())) {
+ AdtConstants.EXT_DEP.equalsIgnoreCase(file.getFileExtension())) {
// This looks to be an extension file.
// For futureproofness let's make sure this dependency file was generated by
// this processor even if it's the only processor using them for now.
@@ -85,8 +86,8 @@ public class RenderScriptProcessor extends SourceProcessor {
// remove the file name segment
relative = relative.removeLastSegments(1);
// add the file name of a Renderscript file.
- relative = relative.append(file.getName().replaceAll(AndroidConstants.RE_DEP_EXT,
- AndroidConstants.DOT_RS));
+ relative = relative.append(file.getName().replaceAll(AdtConstants.RE_DEP_EXT,
+ AdtConstants.DOT_RS));
// now look for a match in the source folders.
List<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(
@@ -122,7 +123,7 @@ public class RenderScriptProcessor extends SourceProcessor {
@Override
protected String getExtension() {
- return AndroidConstants.EXT_RS;
+ return AdtConstants.EXT_RS;
}
@Override
@@ -140,7 +141,7 @@ public class RenderScriptProcessor extends SourceProcessor {
IFolder genFolder = getGenFolder();
IFolder rawFolder = project.getFolder(
- new Path(SdkConstants.FD_RES).append(SdkConstants.FD_RAW));
+ new Path(SdkConstants.FD_RES).append(AndroidConstants.FD_RES_RAW));
int depIndex;
@@ -166,7 +167,7 @@ public class RenderScriptProcessor extends SourceProcessor {
boolean someSuccess = false;
// remove the generic marker from the project
- builder.removeMarkersFromResource(project, AndroidConstants.MARKER_RENDERSCRIPT);
+ builder.removeMarkersFromResource(project, AdtConstants.MARKER_RENDERSCRIPT);
// loop until we've compile them all
for (IFile sourceFile : sources) {
@@ -182,11 +183,11 @@ public class RenderScriptProcessor extends SourceProcessor {
}
// Remove the RS error markers from the source file and the dependencies
- builder.removeMarkersFromResource(sourceFile, AndroidConstants.MARKER_RENDERSCRIPT);
+ builder.removeMarkersFromResource(sourceFile, AdtConstants.MARKER_RENDERSCRIPT);
SourceFileData data = getFileData(sourceFile);
if (data != null) {
for (IFile dep : data.getDependencyFiles()) {
- builder.removeMarkersFromResource(dep, AndroidConstants.MARKER_RENDERSCRIPT);
+ builder.removeMarkersFromResource(dep, AdtConstants.MARKER_RENDERSCRIPT);
}
}
@@ -254,7 +255,7 @@ public class RenderScriptProcessor extends SourceProcessor {
// mark the project
BaseProjectHelper.markResource(project,
- AndroidConstants.MARKER_RENDERSCRIPT,
+ AdtConstants.MARKER_RENDERSCRIPT,
"Unparsed Renderscript error! Check the console for output.",
IMarker.SEVERITY_ERROR);
} else {
@@ -268,7 +269,7 @@ public class RenderScriptProcessor extends SourceProcessor {
String msg = String.format(
"Error executing Renderscript. Please check llvm-rs-cc is present at %1$s",
command[0]);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_RENDERSCRIPT, msg,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_RENDERSCRIPT, msg,
IMarker.SEVERITY_ERROR);
return false;
} catch (InterruptedException e) {
@@ -276,7 +277,7 @@ public class RenderScriptProcessor extends SourceProcessor {
String msg = String.format(
"Error executing Renderscript. Please check llvm-rs-cc is present at %1$s",
command[0]);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_RENDERSCRIPT, msg,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_RENDERSCRIPT, msg,
IMarker.SEVERITY_ERROR);
return false;
}
@@ -344,7 +345,7 @@ public class RenderScriptProcessor extends SourceProcessor {
}
// mark the file
- BaseProjectHelper.markResource(f, AndroidConstants.MARKER_RENDERSCRIPT, msg, line,
+ BaseProjectHelper.markResource(f, AdtConstants.MARKER_RENDERSCRIPT, msg, line,
IMarker.SEVERITY_ERROR);
// success, go to the next line
@@ -417,8 +418,8 @@ public class RenderScriptProcessor extends SourceProcessor {
private IFile getDependencyFileFor(IFile sourceFile) {
IFolder depFolder = getDependencyFolder(sourceFile);
- return depFolder.getFile(sourceFile.getName().replaceAll(AndroidConstants.RE_RS_EXT,
- AndroidConstants.DOT_DEP));
+ return depFolder.getFile(sourceFile.getName().replaceAll(AdtConstants.RE_RS_EXT,
+ AdtConstants.DOT_DEP));
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/BaseBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/BaseBuilder.java
index af4d0ab..293b340 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/BaseBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/BaseBuilder.java
@@ -18,7 +18,7 @@ package com.android.ide.eclipse.adt.internal.build.builders;
import com.android.ide.common.sdk.LoadStatus;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.build.BuildHelper;
import com.android.ide.eclipse.adt.internal.build.Messages;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
@@ -151,7 +151,7 @@ public abstract class BaseBuilder extends IncrementalProjectBuilder {
IFile file = (IFile)resource;
// remove previous markers
- removeMarkersFromResource(file, AndroidConstants.MARKER_XML);
+ removeMarkersFromResource(file, AdtConstants.MARKER_XML);
// create the error handler
XmlErrorHandler reporter = new XmlErrorHandler(file, visitor);
@@ -309,9 +309,9 @@ public abstract class BaseBuilder extends IncrementalProjectBuilder {
}
// abort if there are TARGET or ADT type markers
- stopOnMarker(iProject, AndroidConstants.MARKER_TARGET, IResource.DEPTH_ZERO,
+ stopOnMarker(iProject, AdtConstants.MARKER_TARGET, IResource.DEPTH_ZERO,
false /*checkSeverity*/);
- stopOnMarker(iProject, AndroidConstants.MARKER_ADT, IResource.DEPTH_ZERO,
+ stopOnMarker(iProject, AdtConstants.MARKER_ADT, IResource.DEPTH_ZERO,
false /*checkSeverity*/);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java
index 1d3d13b..71c38f8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.build.builders;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AndroidPrintStream;
import com.android.ide.eclipse.adt.internal.build.AaptExecException;
import com.android.ide.eclipse.adt.internal.build.AaptParser;
@@ -134,7 +134,7 @@ public class PostCompilerBuilder extends BaseBuilder {
int type = resource.getType();
if (type == IResource.FILE) {
String ext = resource.getFileExtension();
- if (AndroidConstants.EXT_CLASS.equals(ext)) {
+ if (AdtConstants.EXT_CLASS.equals(ext)) {
mConvertToDex = true;
}
}
@@ -178,7 +178,7 @@ public class PostCompilerBuilder extends BaseBuilder {
private ResourceMarker mResourceMarker = new ResourceMarker() {
public void setWarning(IResource resource, String message) {
- BaseProjectHelper.markResource(resource, AndroidConstants.MARKER_PACKAGING,
+ BaseProjectHelper.markResource(resource, AdtConstants.MARKER_PACKAGING,
message, IMarker.SEVERITY_WARNING);
}
};
@@ -196,8 +196,8 @@ public class PostCompilerBuilder extends BaseBuilder {
IProject project = getProject();
// Clear the project of the generic markers
- removeMarkersFromContainer(project, AndroidConstants.MARKER_AAPT_PACKAGE);
- removeMarkersFromContainer(project, AndroidConstants.MARKER_PACKAGING);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_AAPT_PACKAGE);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_PACKAGING);
}
// build() returns a list of project from which this project depends for future compilation.
@@ -332,11 +332,11 @@ public class PostCompilerBuilder extends BaseBuilder {
}
// remove older packaging markers.
- removeMarkersFromContainer(javaProject.getProject(), AndroidConstants.MARKER_PACKAGING);
+ removeMarkersFromContainer(javaProject.getProject(), AdtConstants.MARKER_PACKAGING);
if (outputFolder == null) {
// mark project and exit
- markProject(AndroidConstants.MARKER_PACKAGING, Messages.Failed_To_Get_Output,
+ markProject(AdtConstants.MARKER_PACKAGING, Messages.Failed_To_Get_Output,
IMarker.SEVERITY_ERROR);
return allRefProjects;
}
@@ -357,7 +357,7 @@ public class PostCompilerBuilder extends BaseBuilder {
if (mPackageResources == false) {
// check the full resource package
- tmp = outputFolder.findMember(AndroidConstants.FN_RESOURCES_AP_);
+ tmp = outputFolder.findMember(AdtConstants.FN_RESOURCES_AP_);
if (tmp == null || tmp.exists() == false) {
mPackageResources = true;
mBuildFinalPackage = true;
@@ -419,13 +419,13 @@ public class PostCompilerBuilder extends BaseBuilder {
// mark project and exit
String msg = String.format(Messages.s_File_Missing,
SdkConstants.FN_ANDROID_MANIFEST_XML);
- markProject(AndroidConstants.MARKER_PACKAGING, msg, IMarker.SEVERITY_ERROR);
+ markProject(AdtConstants.MARKER_PACKAGING, msg, IMarker.SEVERITY_ERROR);
return allRefProjects;
}
IPath binLocation = outputFolder.getLocation();
if (binLocation == null) {
- markProject(AndroidConstants.MARKER_PACKAGING, Messages.Output_Missing,
+ markProject(AdtConstants.MARKER_PACKAGING, Messages.Output_Missing,
IMarker.SEVERITY_ERROR);
return allRefProjects;
}
@@ -445,14 +445,14 @@ public class PostCompilerBuilder extends BaseBuilder {
// first we check if we need to package the resources.
if (mPackageResources) {
// remove some aapt_package only markers.
- removeMarkersFromContainer(project, AndroidConstants.MARKER_AAPT_PACKAGE);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_AAPT_PACKAGE);
try {
helper.packageResources(manifestFile, libProjects, null /*resfilter*/,
0 /*versionCode */, osBinPath,
- AndroidConstants.FN_RESOURCES_AP_);
+ AdtConstants.FN_RESOURCES_AP_);
} catch (AaptExecException e) {
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_PACKAGING,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_PACKAGING,
e.getMessage(), IMarker.SEVERITY_ERROR);
return allRefProjects;
} catch (AaptResultException e) {
@@ -468,7 +468,7 @@ public class PostCompilerBuilder extends BaseBuilder {
// therefore not all files that should have been marked, were marked),
// we put a generic marker on the project and abort.
BaseProjectHelper.markResource(project,
- AndroidConstants.MARKER_PACKAGING,
+ AdtConstants.MARKER_PACKAGING,
Messages.Unparsed_AAPT_Errors,
IMarker.SEVERITY_ERROR);
}
@@ -493,7 +493,7 @@ public class PostCompilerBuilder extends BaseBuilder {
String message = e.getMessage();
AdtPlugin.printErrorToConsole(project, message);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_PACKAGING,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_PACKAGING,
message, IMarker.SEVERITY_ERROR);
Throwable cause = e.getCause();
@@ -523,7 +523,7 @@ public class PostCompilerBuilder extends BaseBuilder {
SdkConstants.FN_APK_CLASSES_DEX;
try {
helper.finalDebugPackage(
- osBinPath + File.separator + AndroidConstants.FN_RESOURCES_AP_,
+ osBinPath + File.separator + AdtConstants.FN_RESOURCES_AP_,
classesDexPath, osFinalPackagePath,
javaProject, libProjects, referencedJavaProjects, mResourceMarker);
} catch (KeytoolException e) {
@@ -531,7 +531,7 @@ public class PostCompilerBuilder extends BaseBuilder {
// mark the project with the standard message
String msg = String.format(Messages.Final_Archive_Error_s, eMessage);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_PACKAGING, msg,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_PACKAGING, msg,
IMarker.SEVERITY_ERROR);
// output more info in the console
@@ -547,19 +547,19 @@ public class PostCompilerBuilder extends BaseBuilder {
// mark the project with the standard message
String msg = String.format(Messages.Final_Archive_Error_s, eMessage);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_PACKAGING, msg,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_PACKAGING, msg,
IMarker.SEVERITY_ERROR);
} catch (AndroidLocationException e) {
String eMessage = e.getMessage();
// mark the project with the standard message
String msg = String.format(Messages.Final_Archive_Error_s, eMessage);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_PACKAGING, msg,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_PACKAGING, msg,
IMarker.SEVERITY_ERROR);
} catch (NativeLibInJarException e) {
String msg = e.getMessage();
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_PACKAGING,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_PACKAGING,
msg, IMarker.SEVERITY_ERROR);
AdtPlugin.printErrorToConsole(project, (Object[]) e.getAdditionalInfo());
@@ -567,7 +567,7 @@ public class PostCompilerBuilder extends BaseBuilder {
// mark project and return
String msg = String.format(Messages.Final_Archive_Error_s, e.getMessage());
AdtPlugin.printErrorToConsole(project, msg);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_PACKAGING, msg,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_PACKAGING, msg,
IMarker.SEVERITY_ERROR);
} catch (DuplicateFileException e) {
String msg1 = String.format(
@@ -575,7 +575,7 @@ public class PostCompilerBuilder extends BaseBuilder {
e.getArchivePath(), e.getFile1(), e.getFile2());
String msg2 = String.format(Messages.Final_Archive_Error_s, msg1);
AdtPlugin.printErrorToConsole(project, msg2);
- BaseProjectHelper.markResource(project, AndroidConstants.MARKER_PACKAGING, msg2,
+ BaseProjectHelper.markResource(project, AdtConstants.MARKER_PACKAGING, msg2,
IMarker.SEVERITY_ERROR);
}
@@ -618,7 +618,7 @@ public class PostCompilerBuilder extends BaseBuilder {
msg = String.format("Unknown error: %1$s", msg);
AdtPlugin.logAndPrintError(exception, project.getName(), msg);
- markProject(AndroidConstants.MARKER_PACKAGING, msg, IMarker.SEVERITY_ERROR);
+ markProject(AdtConstants.MARKER_PACKAGING, msg, IMarker.SEVERITY_ERROR);
}
return allRefProjects;
@@ -643,13 +643,13 @@ public class PostCompilerBuilder extends BaseBuilder {
// do a (hopefully quick) search for Precompiler type markers. Those are always only
// errors.
- stopOnMarker(iProject, AndroidConstants.MARKER_AAPT_COMPILE, IResource.DEPTH_INFINITE,
+ stopOnMarker(iProject, AdtConstants.MARKER_AAPT_COMPILE, IResource.DEPTH_INFINITE,
false /*checkSeverity*/);
- stopOnMarker(iProject, AndroidConstants.MARKER_AIDL, IResource.DEPTH_INFINITE,
+ stopOnMarker(iProject, AdtConstants.MARKER_AIDL, IResource.DEPTH_INFINITE,
false /*checkSeverity*/);
- stopOnMarker(iProject, AndroidConstants.MARKER_RENDERSCRIPT, IResource.DEPTH_INFINITE,
+ stopOnMarker(iProject, AdtConstants.MARKER_RENDERSCRIPT, IResource.DEPTH_INFINITE,
false /*checkSeverity*/);
- stopOnMarker(iProject, AndroidConstants.MARKER_ANDROID, IResource.DEPTH_ZERO,
+ stopOnMarker(iProject, AdtConstants.MARKER_ANDROID, IResource.DEPTH_ZERO,
false /*checkSeverity*/);
// do a search for JDT markers. Those can be errors or warnings
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerDeltaVisitor.java
index 7df8ef4..7e426fd 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerDeltaVisitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerDeltaVisitor.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.build.builders;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.build.BuildHelper;
import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder.BaseDeltaVisitor;
import com.android.sdklib.SdkConstants;
@@ -177,7 +177,7 @@ public class PostCompilerDeltaVisitor extends BaseDeltaVisitor
// just check this is a .class file. Any modification will
// trigger a change in the classes.dex file
String ext = resource.getFileExtension();
- if (AndroidConstants.EXT_CLASS.equalsIgnoreCase(ext)) {
+ if (AdtConstants.EXT_CLASS.equalsIgnoreCase(ext)) {
mConvertToDex = true;
mMakeFinalPackage = true;
@@ -198,8 +198,8 @@ public class PostCompilerDeltaVisitor extends BaseDeltaVisitor
mConvertToDex = true;
mMakeFinalPackage = true;
} else if (resourceName.equalsIgnoreCase(
- AndroidConstants.FN_RESOURCES_AP_) ||
- AndroidConstants.PATTERN_RESOURCES_S_AP_.matcher(
+ AdtConstants.FN_RESOURCES_AP_) ||
+ AdtConstants.PATTERN_RESOURCES_S_AP_.matcher(
resourceName).matches()) {
// or if the default resources.ap_ or a configured version
// (resources-###.ap_) was removed.
@@ -237,7 +237,7 @@ public class PostCompilerDeltaVisitor extends BaseDeltaVisitor
} else if (mLibFolder != null && mLibFolder.isPrefixOf(path)) {
// inside the native library folder. Test if the changed resource is a .so file.
if (type == IResource.FILE &&
- (AndroidConstants.EXT_NATIVE_LIB.equalsIgnoreCase(path.getFileExtension())
+ (AdtConstants.EXT_NATIVE_LIB.equalsIgnoreCase(path.getFileExtension())
|| SdkConstants.FN_GDBSERVER.equals(resource.getName()))) {
mMakeFinalPackage = true;
return false; // return false for file.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
index fd3a07d..943dcfe 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
@@ -16,13 +16,13 @@
package com.android.ide.eclipse.adt.internal.build.builders;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.build.AaptParser;
import com.android.ide.eclipse.adt.internal.build.AidlProcessor;
-import com.android.ide.eclipse.adt.internal.build.SourceProcessor;
import com.android.ide.eclipse.adt.internal.build.Messages;
import com.android.ide.eclipse.adt.internal.build.RenderScriptProcessor;
+import com.android.ide.eclipse.adt.internal.build.SourceProcessor;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
@@ -303,7 +303,7 @@ public class PreCompilerBuilder extends BaseBuilder {
String msg = String.format(Messages.s_File_Missing,
SdkConstants.FN_ANDROID_MANIFEST_XML);
AdtPlugin.printErrorToConsole(project, msg);
- markProject(AndroidConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
+ markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
return result;
@@ -350,7 +350,7 @@ public class PreCompilerBuilder extends BaseBuilder {
AndroidVersion projectVersion = projectTarget.getVersion();
// remove earlier marker from the manifest
- removeMarkersFromResource(manifestFile, AndroidConstants.MARKER_ADT);
+ removeMarkersFromResource(manifestFile, AdtConstants.MARKER_ADT);
if (minSdkValue != -1) {
String codename = projectVersion.getCodename();
@@ -360,7 +360,7 @@ public class PreCompilerBuilder extends BaseBuilder {
"Platform %1$s is a preview and requires application manifest to set %2$s to '%1$s'",
codename, AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION);
AdtPlugin.printErrorToConsole(project, msg);
- BaseProjectHelper.markResource(manifestFile, AndroidConstants.MARKER_ADT,
+ BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
} else if (minSdkValue < projectVersion.getApiLevel()) {
@@ -370,7 +370,7 @@ public class PreCompilerBuilder extends BaseBuilder {
AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
minSdkValue, projectVersion.getApiLevel());
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, msg);
- BaseProjectHelper.markResource(manifestFile, AndroidConstants.MARKER_ADT,
+ BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_WARNING);
} else if (minSdkValue > projectVersion.getApiLevel()) {
// integer minSdk is too high for the target => warning
@@ -379,7 +379,7 @@ public class PreCompilerBuilder extends BaseBuilder {
AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
minSdkValue, projectVersion.getApiLevel());
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, msg);
- BaseProjectHelper.markResource(manifestFile, AndroidConstants.MARKER_ADT,
+ BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_WARNING);
}
} else {
@@ -392,7 +392,7 @@ public class PreCompilerBuilder extends BaseBuilder {
"Manifest attribute '%1$s' is set to '%2$s'. Integer is expected.",
AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION, minSdkVersion);
AdtPlugin.printErrorToConsole(project, msg);
- BaseProjectHelper.markResource(manifestFile, AndroidConstants.MARKER_ADT,
+ BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
} else if (codename.equals(minSdkVersion) == false) {
@@ -401,7 +401,7 @@ public class PreCompilerBuilder extends BaseBuilder {
"Value of manifest attribute '%1$s' does not match platform codename '%2$s'",
AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION, codename);
AdtPlugin.printErrorToConsole(project, msg);
- BaseProjectHelper.markResource(manifestFile, AndroidConstants.MARKER_ADT,
+ BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
}
@@ -414,7 +414,7 @@ public class PreCompilerBuilder extends BaseBuilder {
"Platform %1$s is a preview and requires application manifests to set %2$s to '%1$s'",
codename, AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION);
AdtPlugin.printErrorToConsole(project, msg);
- BaseProjectHelper.markResource(manifestFile, AndroidConstants.MARKER_ADT, msg,
+ BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT, msg,
IMarker.SEVERITY_ERROR);
return result;
}
@@ -424,7 +424,7 @@ public class PreCompilerBuilder extends BaseBuilder {
String msg = String.format(Messages.s_Doesnt_Declare_Package_Error,
SdkConstants.FN_ANDROID_MANIFEST_XML);
AdtPlugin.printErrorToConsole(project, msg);
- BaseProjectHelper.markResource(manifestFile, AndroidConstants.MARKER_ADT,
+ BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
@@ -434,7 +434,7 @@ public class PreCompilerBuilder extends BaseBuilder {
"Application package '%1$s' must have a minimum of 2 segments.",
SdkConstants.FN_ANDROID_MANIFEST_XML);
AdtPlugin.printErrorToConsole(project, msg);
- BaseProjectHelper.markResource(manifestFile, AndroidConstants.MARKER_ADT,
+ BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
@@ -526,41 +526,49 @@ public class PreCompilerBuilder extends BaseBuilder {
// remove all the derived resources from the 'gen' source folder.
if (mGenFolder != null) {
+ // gen folder should not be derived, but previous version could set it to derived
+ // so we make sure this isn't the case (or it'll get deleted by the clean)
+ mGenFolder.setDerived(false);
+
removeDerivedResources(mGenFolder, monitor);
}
// Clear the project of the generic markers
- removeMarkersFromContainer(project, AndroidConstants.MARKER_AAPT_COMPILE);
- removeMarkersFromContainer(project, AndroidConstants.MARKER_XML);
- removeMarkersFromContainer(project, AndroidConstants.MARKER_AIDL);
- removeMarkersFromContainer(project, AndroidConstants.MARKER_RENDERSCRIPT);
- removeMarkersFromContainer(project, AndroidConstants.MARKER_ANDROID);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_AAPT_COMPILE);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_XML);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_AIDL);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_RENDERSCRIPT);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_ANDROID);
}
@Override
protected void startupOnInitialize() {
- super.startupOnInitialize();
-
- IProject project = getProject();
+ try {
+ super.startupOnInitialize();
- // load the previous IFolder and java package.
- mManifestPackage = loadProjectStringProperty(PROPERTY_PACKAGE);
+ IProject project = getProject();
- // get the source folder in which all the Java files are created
- mGenFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES);
+ // load the previous IFolder and java package.
+ mManifestPackage = loadProjectStringProperty(PROPERTY_PACKAGE);
- // Load the current compile flags. We ask for true if not found to force a recompile.
- mMustCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true);
+ // get the source folder in which all the Java files are created
+ mGenFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES);
+ mDerivedProgressMonitor = new DerivedProgressMonitor(mGenFolder);
- IJavaProject javaProject = JavaCore.create(project);
+ // Load the current compile flags. We ask for true if not found to force a recompile.
+ mMustCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true);
- // load the source processors
- SourceProcessor aidlProcessor = new AidlProcessor(javaProject, mGenFolder);
- mProcessors.add(aidlProcessor);
- SourceProcessor renderScriptProcessor = new RenderScriptProcessor(javaProject, mGenFolder);
- mProcessors.add(renderScriptProcessor);
+ IJavaProject javaProject = JavaCore.create(project);
- mDerivedProgressMonitor = new DerivedProgressMonitor(mGenFolder);
+ // load the source processors
+ SourceProcessor aidlProcessor = new AidlProcessor(javaProject, mGenFolder);
+ mProcessors.add(aidlProcessor);
+ SourceProcessor renderScriptProcessor = new RenderScriptProcessor(javaProject,
+ mGenFolder);
+ mProcessors.add(renderScriptProcessor);
+ } catch (Throwable throwable) {
+ AdtPlugin.log(throwable, "Failed to finish PrecompilerBuilder#startupOnInitialize()");
+ }
}
/**
@@ -576,7 +584,7 @@ public class PreCompilerBuilder extends BaseBuilder {
private void handleResources(IProject project, String javaPackage, IAndroidTarget projectTarget,
IFile manifest, List<IProject> libProjects) throws CoreException, AbortBuildException {
// get the resource folder
- IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES);
+ IFolder resFolder = project.getFolder(AdtConstants.WS_RESOURCES);
// get the file system path
IPath outputLocation = mGenFolder.getLocation();
@@ -591,8 +599,8 @@ public class PreCompilerBuilder extends BaseBuilder {
String osManifestPath = manifestLocation.toOSString();
// remove the aapt markers
- removeMarkersFromResource(manifest, AndroidConstants.MARKER_AAPT_COMPILE);
- removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT_COMPILE);
+ removeMarkersFromResource(manifest, AdtConstants.MARKER_AAPT_COMPILE);
+ removeMarkersFromContainer(resFolder, AdtConstants.MARKER_AAPT_COMPILE);
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.Preparing_Generated_Files);
@@ -661,7 +669,7 @@ public class PreCompilerBuilder extends BaseBuilder {
// We actually need to delete the manifest.java as it may become empty and
// in this case aapt doesn't generate an empty one, but instead doesn't
// touch it.
- IFile manifestJavaFile = packageFolder.getFile(AndroidConstants.FN_MANIFEST_CLASS);
+ IFile manifestJavaFile = packageFolder.getFile(AdtConstants.FN_MANIFEST_CLASS);
manifestJavaFile.getLocation().toFile().delete();
// launch aapt: create the command line
@@ -737,7 +745,7 @@ public class PreCompilerBuilder extends BaseBuilder {
// (and therefore not all files that should have been marked,
// were marked), we put a generic marker on the project and abort.
if (parsingError) {
- markProject(AndroidConstants.MARKER_ADT,
+ markProject(AdtConstants.MARKER_ADT,
Messages.Unparsed_AAPT_Errors, IMarker.SEVERITY_ERROR);
}
@@ -751,7 +759,7 @@ public class PreCompilerBuilder extends BaseBuilder {
// something happen while executing the process,
// mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error, array.get(0));
- markProject(AndroidConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
+ markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// This interrupts the build.
throw new AbortBuildException();
@@ -759,7 +767,7 @@ public class PreCompilerBuilder extends BaseBuilder {
// we got interrupted waiting for the process to end...
// mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error, array.get(0));
- markProject(AndroidConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
+ markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// This interrupts the build.
throw new AbortBuildException();
@@ -783,11 +791,11 @@ public class PreCompilerBuilder extends BaseBuilder {
*/
private IPath getJavaPackagePath(String javaPackageName) {
// convert the java package into path
- String[] segments = javaPackageName.split(AndroidConstants.RE_DOT);
+ String[] segments = javaPackageName.split(AdtConstants.RE_DOT);
StringBuilder path = new StringBuilder();
for (String s : segments) {
- path.append(AndroidConstants.WS_SEP_CHAR);
+ path.append(AdtConstants.WS_SEP_CHAR);
path.append(s);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java
index 6660cd2..cd99fbe 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.build.builders;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.build.SourceChangeHandler;
import com.android.ide.eclipse.adt.internal.build.SourceProcessor;
import com.android.ide.eclipse.adt.internal.build.Messages;
@@ -199,9 +199,9 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta
IFile manifestFile = (IFile)resource;
if (manifestFile.exists()) {
- manifestFile.deleteMarkers(AndroidConstants.MARKER_XML, true,
+ manifestFile.deleteMarkers(AdtConstants.MARKER_XML, true,
IResource.DEPTH_ZERO);
- manifestFile.deleteMarkers(AndroidConstants.MARKER_ANDROID, true,
+ manifestFile.deleteMarkers(AdtConstants.MARKER_ANDROID, true,
IResource.DEPTH_ZERO);
}
@@ -262,8 +262,8 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta
String fileName = resource.getName();
// Special case of R.java/Manifest.java.
- if (AndroidConstants.FN_RESOURCE_CLASS.equals(fileName) ||
- AndroidConstants.FN_MANIFEST_CLASS.equals(fileName)) {
+ if (AdtConstants.FN_RESOURCE_CLASS.equals(fileName) ||
+ AdtConstants.FN_MANIFEST_CLASS.equals(fileName)) {
// if it was removed, there's a possibility that it was removed due to a
// package change, or an aidl that was removed, but the only thing
// that will happen is that we'll have an extra build. Not much of a problem.
@@ -324,17 +324,17 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta
case IResourceDelta.CHANGED:
// display verbose message
message = String.format(Messages.s_Modified_Recreating_s, p,
- AndroidConstants.FN_RESOURCE_CLASS);
+ AdtConstants.FN_RESOURCE_CLASS);
break;
case IResourceDelta.ADDED:
// display verbose message
message = String.format(Messages.Added_s_s_Needs_Updating, p,
- AndroidConstants.FN_RESOURCE_CLASS);
+ AdtConstants.FN_RESOURCE_CLASS);
break;
case IResourceDelta.REMOVED:
// display verbose message
message = String.format(Messages.s_Removed_s_Needs_Updating, p,
- AndroidConstants.FN_RESOURCE_CLASS);
+ AdtConstants.FN_RESOURCE_CLASS);
break;
}
if (message != null) {
@@ -346,7 +346,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta
handler.handleResourceFile((IFile)resource, kind);
}
- if (AndroidConstants.EXT_XML.equalsIgnoreCase(ext)) {
+ if (AdtConstants.EXT_XML.equalsIgnoreCase(ext)) {
if (kind != IResourceDelta.REMOVED) {
// check xml Validity
mBuilder.checkXML(resource, this);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ResourceManagerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ResourceManagerBuilder.java
index 0908260..950200a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ResourceManagerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ResourceManagerBuilder.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.build.builders;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.build.Messages;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
@@ -64,7 +64,7 @@ public class ResourceManagerBuilder extends BaseBuilder {
IProject project = getProject();
// Clear the project of the generic markers
- removeMarkersFromContainer(project, AndroidConstants.MARKER_ADT);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_ADT);
}
// build() returns a list of project from which this project depends for future compilation.
@@ -77,7 +77,7 @@ public class ResourceManagerBuilder extends BaseBuilder {
IJavaProject javaProject = JavaCore.create(project);
// Clear the project of the generic markers
- removeMarkersFromContainer(project, AndroidConstants.MARKER_ADT);
+ removeMarkersFromContainer(project, AdtConstants.MARKER_ADT);
// check for existing target marker, in which case we abort.
// (this means: no SDK, no target, or unresolvable target.)
@@ -104,7 +104,7 @@ public class ResourceManagerBuilder extends BaseBuilder {
}
if (errorMessage != null) {
- markProject(AndroidConstants.MARKER_ADT, errorMessage, IMarker.SEVERITY_ERROR);
+ markProject(AdtConstants.MARKER_ADT, errorMessage, IMarker.SEVERITY_ERROR);
AdtPlugin.printErrorToConsole(project, errorMessage);
return null;
@@ -115,7 +115,7 @@ public class ResourceManagerBuilder extends BaseBuilder {
if (osSdkFolder == null || osSdkFolder.length() == 0) {
AdtPlugin.printErrorToConsole(project, Messages.No_SDK_Setup_Error);
- markProject(AndroidConstants.MARKER_ADT, Messages.No_SDK_Setup_Error,
+ markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error,
IMarker.SEVERITY_ERROR);
return null;
@@ -166,7 +166,7 @@ public class ResourceManagerBuilder extends BaseBuilder {
}
AdtPlugin.printErrorToConsole(project, message);
- markProject(AndroidConstants.MARKER_ADT, message, IMarker.SEVERITY_ERROR);
+ markProject(AdtConstants.MARKER_ADT, message, IMarker.SEVERITY_ERROR);
return null;
} else if (hasGenSrcFolder == false || genFolderPresent == false) {
@@ -191,7 +191,6 @@ public class ResourceManagerBuilder extends BaseBuilder {
"Creating 'gen' source folder for generated Java files");
genFolder.create(true /* force */, true /* local */,
new SubProgressMonitor(monitor, 10));
- genFolder.setDerived(true);
}
// add it to the source folder list, if needed only (or it will throw)
@@ -214,12 +213,12 @@ public class ResourceManagerBuilder extends BaseBuilder {
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, Messages.Refreshing_Res);
// refresh the res folder.
- IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES);
+ IFolder resFolder = project.getFolder(AdtConstants.WS_RESOURCES);
resFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
// Also refresh the assets folder to make sure the ApkBuilder
// will now it's changed and will force a new resource packaging.
- IFolder assetsFolder = project.getFolder(AndroidConstants.WS_ASSETS);
+ IFolder assetsFolder = project.getFolder(AdtConstants.WS_ASSETS);
assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java
index 4c542b1..17ffe30 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java
@@ -16,6 +16,12 @@
package com.android.ide.eclipse.adt.internal.editors;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor.ATTRIBUTE_ICON_FILENAME;
+
+import com.android.ide.common.api.IAttributeInfo;
+import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
@@ -25,15 +31,19 @@ import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttribu
import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.TextValueDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiFlagAttributeNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiResourceAttributeNode;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.sdklib.SdkConstants;
+import com.android.util.Pair;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.contentassist.CompletionProposal;
@@ -44,21 +54,32 @@ import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.graphics.Image;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PlatformUI;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.regex.Pattern;
/**
* Content Assist Processor for Android XML files
+ * <p>
+ * Remaining corner cases:
+ * <ul>
+ * <li>Completion does not work right if there is a space between the = and the opening
+ * quote.
+ * <li>Replacement completion does not work right if the caret is to the left of the
+ * opening quote, where the opening quote is a single quote, and the replacement items use
+ * double quotes.
+ * </ul>
*/
+@SuppressWarnings("restriction") // XML model
public abstract class AndroidContentAssist implements IContentAssistProcessor {
/** Regexp to detect a full attribute after an element tag.
@@ -72,7 +93,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
"^ *[a-zA-Z_:]+ *= *(?:\"[^<\"]*\"|'[^<']*')"); //$NON-NLS-1$
/** Regexp to detect an element tag name */
- private static Pattern sFirstElementWord = Pattern.compile("^[a-zA-Z0-9_:]+"); //$NON-NLS-1$
+ private static Pattern sFirstElementWord = Pattern.compile("^[a-zA-Z0-9_:-]+"); //$NON-NLS-1$
/** Regexp to detect whitespace */
private static Pattern sWhitespace = Pattern.compile("\\s+"); //$NON-NLS-1$
@@ -86,7 +107,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
private final int mDescriptorId;
- private AndroidXmlEditor mEditor;
+ protected AndroidXmlEditor mEditor;
/**
* Constructor for AndroidContentAssist
@@ -113,9 +134,10 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
+ String wordPrefix = extractElementPrefix(viewer, offset);
if (mEditor == null) {
- mEditor = getAndroidXmlEditor(viewer);
+ mEditor = AndroidXmlEditor.getAndroidXmlEditor(viewer);
if (mEditor == null) {
// This should not happen. Duck and forget.
AdtPlugin.log(IStatus.ERROR, "Editor not found during completion");
@@ -123,83 +145,147 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
}
}
- UiElementNode rootUiNode = mEditor.getUiRootNode();
-
- Object[] choices = null; /* An array of ElementDescriptor, or AttributeDescriptor
- or String or null */
- String parent = ""; //$NON-NLS-1$
- String wordPrefix = extractElementPrefix(viewer, offset);
- char needTag = 0;
- boolean isElement = false;
- boolean isAttribute = false;
-
- Node currentNode = getNode(viewer, offset);
- if (currentNode == null)
- return null;
-
- // check to see if we can find a UiElementNode matching this XML node
- UiElementNode currentUiNode =
- rootUiNode == null ? null : rootUiNode.findXmlNode(currentNode);
+ // List of proposals, in the order presented to the user.
+ List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(80);
- if (currentNode == null) {
- // Should not happen (an XML doc always has at least a doc node). Just give up.
+ // Look up the caret context - where in an element, or between elements, or
+ // within an element's children, is the given caret offset located?
+ Pair<Node, Node> context = DomUtilities.getNodeContext(viewer.getDocument(), offset);
+ if (context == null) {
return null;
}
+ Node parentNode = context.getFirst();
+ Node currentNode = context.getSecond();
+ assert parentNode != null || currentNode != null;
- if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
- parent = currentNode.getNodeName();
-
- if (wordPrefix.equals(parent)) {
- // We are still editing the element's tag name, not the attributes
- // (the element's tag name may not even be complete)
- isElement = true;
- choices = getChoicesForElement(parent, currentNode);
+ UiElementNode rootUiNode = mEditor.getUiRootNode();
+ if (currentNode == null || currentNode.getNodeType() == Node.TEXT_NODE) {
+ UiElementNode parentUiNode =
+ rootUiNode == null ? null : rootUiNode.findXmlNode(parentNode);
+ computeTextValues(proposals, offset, parentNode, currentNode, parentUiNode,
+ wordPrefix);
+ } else if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ String parent = currentNode.getNodeName();
+ AttribInfo info = parseAttributeInfo(viewer, offset, offset - wordPrefix.length());
+ char nextChar = extractChar(viewer, offset);
+ if (info != null) {
+ // check to see if we can find a UiElementNode matching this XML node
+ UiElementNode currentUiNode = rootUiNode == null
+ ? null : rootUiNode.findXmlNode(currentNode);
+ computeAttributeProposals(proposals, viewer, offset, wordPrefix, currentUiNode,
+ parentNode, currentNode, parent, info, nextChar);
} else {
- // We're not editing the current node name, so we might be editing its
- // attributes instead...
- isAttribute = true;
- AttribInfo info = parseAttributeInfo(viewer, offset);
- if (info != null) {
- // We're editing attributes in an element node (either the attributes' names
- // or their values).
- choices = getChoicesForAttribute(parent, currentNode, currentUiNode, info);
-
- if (info.correctedPrefix != null) {
- wordPrefix = info.correctedPrefix;
- }
- needTag = info.needTag;
- }
+ computeNonAttributeProposals(viewer, offset, wordPrefix, proposals, parentNode,
+ currentNode, parent, nextChar);
}
- } else if (currentNode.getNodeType() == Node.TEXT_NODE) {
- isElement = true;
- // Examine the parent of the text node.
- choices = getChoicesForTextNode(currentNode);
}
- // Abort if we can't recognize the context or there are no completion choices
- if (choices == null || choices.length == 0) return null;
+ return proposals.toArray(new ICompletionProposal[proposals.size()]);
+ }
+
+ private void computeNonAttributeProposals(ITextViewer viewer, int offset, String wordPrefix,
+ List<ICompletionProposal> proposals, Node parentNode, Node currentNode, String parent,
+ char nextChar) {
+ if (startsWith(parent, wordPrefix)) {
+ // We are still editing the element's tag name, not the attributes
+ // (the element's tag name may not even be complete)
+
+ Object[] choices = getChoicesForElement(parent, currentNode);
+ if (choices == null || choices.length == 0) {
+ return;
+ }
+
+ int selectionLength = getSelectionLength(viewer);
+ int replaceLength = parent.length() - wordPrefix.length();
+ boolean isNew = replaceLength == 0 && nextNonspaceChar(viewer, offset) == '<';
+ // Special case: if we are right before the beginning of a new
+ // element, wipe out the replace length such that we insert before it,
+ // we don't edit the current element.
+ if (wordPrefix.length() == 0 && nextChar == '<') {
+ replaceLength = 0;
+ isNew = true;
+ }
- if (isElement) {
// If we found some suggestions, do we need to add an opening "<" bracket
// for the element? We don't if the cursor is right after "<" or "</".
// Per XML Spec, there's no whitespace between "<" or "</" and the tag name.
- int offset2 = offset - wordPrefix.length() - 1;
- int c1 = extractChar(viewer, offset2);
- if (!((c1 == '<') || (c1 == '/' && extractChar(viewer, offset2 - 1) == '<'))) {
- needTag = '<';
- }
+ char needTag = computeElementNeedTag(viewer, offset, wordPrefix);
+
+ addMatchingProposals(proposals, choices, offset,
+ parentNode != null ? parentNode : null, wordPrefix, needTag,
+ false /* isAttribute */, isNew, false /*isComplete*/,
+ selectionLength + replaceLength);
}
+ }
+ private int getSelectionLength(ITextViewer viewer) {
// get the selection length
int selectionLength = 0;
ISelection selection = viewer.getSelectionProvider().getSelection();
if (selection instanceof TextSelection) {
- TextSelection textSelection = (TextSelection)selection;
+ TextSelection textSelection = (TextSelection) selection;
selectionLength = textSelection.getLength();
}
+ return selectionLength;
+ }
+
+ private void computeAttributeProposals(List<ICompletionProposal> proposals, ITextViewer viewer,
+ int offset, String wordPrefix, UiElementNode currentUiNode, Node parentNode,
+ Node currentNode, String parent, AttribInfo info, char nextChar) {
+ // We're editing attributes in an element node (either the attributes' names
+ // or their values).
+
+ if (info.isInValue) {
+ computeAttributeValues(proposals, offset, parent, info.name, currentNode,
+ wordPrefix, info.skipEndTag, info.replaceLength);
+ }
+
+ // Look up attribute proposals based on descriptors
+ Object[] choices = getChoicesForAttribute(parent, currentNode, currentUiNode,
+ info, wordPrefix);
+ if (choices == null || choices.length == 0) {
+ return;
+ }
- return computeProposals(offset, currentNode, choices, wordPrefix, needTag,
- isAttribute, selectionLength);
+ int selectionLength = getSelectionLength(viewer);
+ int replaceLength = info.replaceLength;
+ if (info.correctedPrefix != null) {
+ wordPrefix = info.correctedPrefix;
+ }
+ char needTag = info.needTag;
+ // Look to the right and see if we're followed by whitespace
+ boolean isNew = replaceLength == 0
+ && (Character.isWhitespace(nextChar) || nextChar == '>' || nextChar == '/');
+
+ addMatchingProposals(proposals, choices, offset, parentNode != null ? parentNode : null,
+ wordPrefix, needTag, true /* isAttribute */, isNew, info.skipEndTag,
+ selectionLength + replaceLength);
+ }
+
+ private char computeElementNeedTag(ITextViewer viewer, int offset, String wordPrefix) {
+ char needTag = 0;
+ int offset2 = offset - wordPrefix.length() - 1;
+ char c1 = extractChar(viewer, offset2);
+ if (!((c1 == '<') || (c1 == '/' && extractChar(viewer, offset2 - 1) == '<'))) {
+ needTag = '<';
+ }
+ return needTag;
+ }
+
+ protected int computeTextReplaceLength(Node currentNode, int offset) {
+ if (currentNode == null) {
+ return 0;
+ }
+
+ assert currentNode != null && currentNode.getNodeType() == Node.TEXT_NODE;
+
+ String nodeValue = currentNode.getNodeValue();
+ int relativeOffset = offset - ((IndexedRegion) currentNode).getStartOffset();
+ int lineEnd = nodeValue.indexOf('\n', relativeOffset);
+ if (lineEnd == -1) {
+ lineEnd = nodeValue.length();
+ }
+ return lineEnd - relativeOffset;
}
/**
@@ -211,7 +297,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
* e.g. {@link SdkConstants#NS_RESOURCES}
* @return The first prefix declared or the default "android" prefix.
*/
- private String lookupNamespacePrefix(Node node, String nsUri) {
+ private static String lookupNamespacePrefix(Node node, String nsUri) {
// Note: Node.lookupPrefix is not implemented in wst/xml/core NodeImpl.java
// The following emulates this:
// String prefix = node.lookupPrefix(SdkConstants.NS_RESOURCES);
@@ -273,7 +359,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
if (grandparent != null) {
for (ElementDescriptor e : grandparent.getChildren()) {
if (e.getXmlName().startsWith(parent)) {
- return grandparent.getChildren();
+ return sort(grandparent.getChildren());
}
}
}
@@ -281,6 +367,25 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
return null;
}
+ /** Non-destructively sort a list of ElementDescriptors and return the result */
+ private static ElementDescriptor[] sort(ElementDescriptor[] elements) {
+ if (elements != null && elements.length > 1) {
+ // Sort alphabetically. Must make copy to not destroy original.
+ ElementDescriptor[] copy = new ElementDescriptor[elements.length];
+ System.arraycopy(elements, 0, copy, 0, elements.length);
+
+ Arrays.sort(copy, new Comparator<ElementDescriptor>() {
+ public int compare(ElementDescriptor e1, ElementDescriptor e2) {
+ return e1.getXmlLocalName().compareTo(e2.getXmlLocalName());
+ }
+ });
+
+ return copy;
+ }
+
+ return elements;
+ }
+
/**
* Gets the choices when the user is editing an XML attribute.
* <p/>
@@ -302,13 +407,14 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
* a String[] if the user is editing an attribute value with some known values,
* or null if nothing is known about the context.
*/
- private Object[] getChoicesForAttribute(String parent,
- Node currentNode, UiElementNode currentUiNode, AttribInfo attrInfo) {
+ private Object[] getChoicesForAttribute(
+ String parent, Node currentNode, UiElementNode currentUiNode, AttribInfo attrInfo,
+ String wordPrefix) {
Object[] choices = null;
if (attrInfo.isInValue) {
// Editing an attribute's value... Get the attribute name and then the
// possible choices for the tuple(parent,attribute)
- String value = attrInfo.value;
+ String value = attrInfo.valuePrefix;
if (value.startsWith("'") || value.startsWith("\"")) { //$NON-NLS-1$ //$NON-NLS-2$
value = value.substring(1);
// The prefix that was found at the beginning only scan for characters
@@ -337,18 +443,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
}
if (currAttrNode != null) {
- choices = currAttrNode.getPossibleValues(value);
-
- if (currAttrNode instanceof UiFlagAttributeNode) {
- // A "flag" can consist of several values separated by "or" (|).
- // If the correct prefix contains such a pipe character, we change
- // it so that only the currently edited value is completed.
- pos = value.indexOf('|');
- if (pos >= 0) {
- attrInfo.correctedPrefix = value = value.substring(pos + 1);
- attrInfo.needTag = 0;
- }
- }
+ choices = getAttributeValueChoices(currAttrNode, attrInfo, value);
}
}
@@ -385,6 +480,85 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
return choices;
}
+ protected Object[] getAttributeValueChoices(UiAttributeNode currAttrNode, AttribInfo attrInfo,
+ String value) {
+ Object[] choices;
+ int pos;
+ choices = currAttrNode.getPossibleValues(value);
+ if (choices != null && currAttrNode instanceof UiResourceAttributeNode) {
+ attrInfo.skipEndTag = false;
+ }
+
+ if (currAttrNode instanceof UiFlagAttributeNode) {
+ // A "flag" can consist of several values separated by "or" (|).
+ // If the correct prefix contains such a pipe character, we change
+ // it so that only the currently edited value is completed.
+ pos = value.lastIndexOf('|');
+ if (pos >= 0) {
+ attrInfo.correctedPrefix = value = value.substring(pos + 1);
+ attrInfo.needTag = 0;
+ }
+
+ attrInfo.skipEndTag = false;
+ }
+
+ // Should we do suffix completion on dimension units etc?
+ choices = completeSuffix(choices, value, currAttrNode);
+
+ // Check to see if the user is attempting resource completion
+ AttributeDescriptor attributeDescriptor = currAttrNode.getDescriptor();
+ IAttributeInfo attributeInfo = attributeDescriptor.getAttributeInfo();
+ if (value.startsWith(PREFIX_RESOURCE_REF)
+ && !Format.REFERENCE.in(attributeInfo.getFormats())) {
+ // Special case: If the attribute value looks like a reference to a
+ // resource, offer to complete it, since in many cases our metadata
+ // does not correctly state whether a resource value is allowed. We don't
+ // offer these for an empty completion context, but if the user has
+ // actually typed "@", in that case list resource matches.
+ // For example, for android:minHeight this makes completion on @dimen/
+ // possible.
+ choices = UiResourceAttributeNode.computeResourceStringMatches(
+ mEditor, attributeDescriptor, value);
+ attrInfo.skipEndTag = false;
+ }
+
+ return choices;
+ }
+
+ protected void computeAttributeValues(List<ICompletionProposal> proposals, int offset,
+ String parentTagName, String attributeName, Node node, String wordPrefix,
+ boolean skipEndTag, int replaceLength) {
+ }
+
+ protected void computeTextValues(List<ICompletionProposal> proposals, int offset,
+ Node parentNode, Node currentNode, UiElementNode uiParent,
+ String wordPrefix) {
+
+ if (parentNode != null) {
+ // Examine the parent of the text node.
+ Object[] choices = getElementChoicesForTextNode(parentNode);
+ if (choices != null && choices.length > 0) {
+ ISourceViewer viewer = mEditor.getStructuredSourceViewer();
+ char needTag = computeElementNeedTag(viewer, offset, wordPrefix);
+
+ // get the selection length
+ int selectionLength = 0;
+ ISelection selection = viewer.getSelectionProvider().getSelection();
+ if (selection instanceof TextSelection) {
+ TextSelection textSelection = (TextSelection) selection;
+ selectionLength = textSelection.getLength();
+ }
+ int replaceLength = 0;
+ addMatchingProposals(proposals, choices,
+ offset, parentNode != null ? parentNode : null, wordPrefix, needTag,
+ false /* isAttribute */,
+ false /*isNew*/,
+ false /*isComplete*/,
+ selectionLength + replaceLength);
+ }
+ }
+ }
+
/**
* Gets the choices when the user is editing an XML text node.
* <p/>
@@ -394,42 +568,46 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
*
* @return An ElementDescriptor[] or null.
*/
- private Object[] getChoicesForTextNode(Node currentNode) {
+ private Object[] getElementChoicesForTextNode(Node parentNode) {
Object[] choices = null;
String parent;
- Node parent_node = currentNode.getParentNode();
- if (parent_node.getNodeType() == Node.ELEMENT_NODE) {
+ if (parentNode.getNodeType() == Node.ELEMENT_NODE) {
// We're editing a text node which parent is an element node. Limit
// content assist to elements valid for the parent.
- parent = parent_node.getNodeName();
+ parent = parentNode.getNodeName();
ElementDescriptor desc = getDescriptor(parent);
if (desc != null) {
- choices = desc.getChildren();
+ choices = sort(desc.getChildren());
}
- } else if (parent_node.getNodeType() == Node.DOCUMENT_NODE) {
+ } else if (parentNode.getNodeType() == Node.DOCUMENT_NODE) {
// We're editing a text node at the first level (i.e. root node).
// Limit content assist to the only valid root elements.
- choices = getRootDescriptor().getChildren();
+ choices = sort(getRootDescriptor().getChildren());
}
+
return choices;
}
- /**
- * Given a list of choices found, generates the proposals to be displayed to the user.
+ /**
+ * Given a list of choices, adds in any that match the current prefix into the
+ * proposals list.
* <p/>
* Choices is an object array. Items of the array can be:
* - ElementDescriptor: a possible element descriptor which XML name should be completed.
* - AttributeDescriptor: a possible attribute descriptor which XML name should be completed.
* - String: string values to display as-is to the user. Typically those are possible
* values for a given attribute.
- *
- * @return The ICompletionProposal[] to display to the user.
+ * - Pair of Strings: the first value is the keyword to insert, and the second value
+ * is the tooltip/help for the value to be displayed in the documentation popup.
*/
- private ICompletionProposal[] computeProposals(int offset, Node currentNode,
- Object[] choices, String wordPrefix, char need_tag,
- boolean is_attribute, int selectionLength) {
- ArrayList<CompletionProposal> proposals = new ArrayList<CompletionProposal>();
- HashMap<String, String> nsUriMap = new HashMap<String, String>();
+ protected void addMatchingProposals(List<ICompletionProposal> proposals, Object[] choices,
+ int offset, Node currentNode, String wordPrefix, char needTag,
+ boolean isAttribute, boolean isNew, boolean skipEndTag, int replaceLength) {
+ if (choices == null) {
+ return;
+ }
+
+ Map<String, String> nsUriMap = new HashMap<String, String>();
for (Object choice : choices) {
String keyword = null;
@@ -438,7 +616,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
String tooltip = null;
if (choice instanceof ElementDescriptor) {
keyword = ((ElementDescriptor)choice).getXmlName();
- icon = ((ElementDescriptor)choice).getIcon();
+ icon = ((ElementDescriptor)choice).getGenericIcon();
tooltip = DescriptorsUtils.formatTooltip(((ElementDescriptor)choice).getTooltip());
} else if (choice instanceof TextValueDescriptor) {
continue; // Value nodes are not part of the completion choices
@@ -446,7 +624,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
continue; // not real attribute descriptors
} else if (choice instanceof AttributeDescriptor) {
keyword = ((AttributeDescriptor)choice).getXmlLocalName();
- icon = ((AttributeDescriptor)choice).getIcon();
+ icon = ((AttributeDescriptor)choice).getGenericIcon();
if (choice instanceof TextAttributeDescriptor) {
tooltip = ((TextAttributeDescriptor) choice).getTooltip();
}
@@ -467,49 +645,180 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
} else if (choice instanceof String) {
keyword = (String) choice;
+ if (isAttribute) {
+ icon = IconFactory.getInstance().getIcon(ATTRIBUTE_ICON_FILENAME);
+ }
+ } else if (choice instanceof Pair<?, ?>) {
+ @SuppressWarnings("unchecked")
+ Pair<String, String> pair = (Pair<String, String>) choice;
+ keyword = pair.getFirst();
+ tooltip = pair.getSecond();
+ if (isAttribute) {
+ icon = IconFactory.getInstance().getIcon(ATTRIBUTE_ICON_FILENAME);
+ }
} else {
continue; // discard unknown choice
}
String nsKeyword = nsPrefix == null ? keyword : (nsPrefix + keyword);
- if (keyword.startsWith(wordPrefix) ||
- (nsPrefix != null && keyword.startsWith(nsPrefix)) ||
- (nsPrefix != null && nsKeyword.startsWith(wordPrefix))) {
- if (nsPrefix != null) {
- keyword = nsPrefix + keyword;
- }
- String end_tag = ""; //$NON-NLS-1$
- if (need_tag != 0) {
- if (need_tag == '"') {
- keyword = need_tag + keyword;
- end_tag = String.valueOf(need_tag);
- } else if (need_tag == '<') {
+ if (nameStartsWith(nsKeyword, wordPrefix, nsPrefix)) {
+ keyword = nsKeyword;
+ String endTag = ""; //$NON-NLS-1$
+ if (needTag != 0) {
+ if (needTag == '"') {
+ keyword = needTag + keyword;
+ endTag = String.valueOf(needTag);
+ } else if (needTag == '<') {
if (elementCanHaveChildren(choice)) {
- end_tag = String.format("></%1$s>", keyword); //$NON-NLS-1$
- keyword = need_tag + keyword;
+ endTag = String.format("></%1$s>", keyword); //$NON-NLS-1$
} else {
- keyword = need_tag + keyword;
- end_tag = "/>"; //$NON-NLS-1$
+ endTag = "/>"; //$NON-NLS-1$
}
+ keyword = needTag + keyword + ' ';
+ } else if (needTag == ' ') {
+ keyword = needTag + keyword;
+ }
+ } else if (!isAttribute && isNew) {
+ if (elementCanHaveChildren(choice)) {
+ endTag = String.format("></%1$s>", keyword); //$NON-NLS-1$
+ } else {
+ endTag = "/>"; //$NON-NLS-1$
}
+ keyword = keyword + ' ';
}
- CompletionProposal proposal = new CompletionProposal(
- keyword + end_tag, // String replacementString
- offset - wordPrefix.length(), // int replacementOffset
- wordPrefix.length() + selectionLength, // int replacementLength
- keyword.length(), // int cursorPosition (rel. to rplcmntOffset)
- icon, // Image image
- null, // String displayString
- null, // IContextInformation contextInformation
- tooltip // String additionalProposalInfo
- );
-
- proposals.add(proposal);
+
+ final String suffix;
+ int cursorPosition;
+ final String displayString;
+ if (choice instanceof AttributeDescriptor && isNew) {
+ // Special case for attributes: insert ="" stuff and locate caret inside ""
+ suffix = "=\"\""; //$NON-NLS-1$
+ cursorPosition = keyword.length() + suffix.length() - 1;
+ displayString = keyword + endTag; // don't include suffix;
+ } else {
+ suffix = endTag;
+ cursorPosition = keyword.length();
+ displayString = null;
+ }
+
+ if (skipEndTag) {
+ assert isAttribute;
+ cursorPosition++;
+ }
+
+ // For attributes, automatically insert ns:attribute="" and place the cursor
+ // inside the quotes.
+ // Special case for attributes: insert ="" stuff and locate caret inside ""
+ proposals.add(new CompletionProposal(
+ keyword + suffix , // String replacementString
+ offset - wordPrefix.length(), // int replacementOffset
+ wordPrefix.length() + replaceLength,// int replacementLength
+ cursorPosition, // cursorPosition
+ icon, // Image image
+ displayString, // displayString
+ null, // IContextInformation contextInformation
+ tooltip // String additionalProposalInfo
+ ));
}
}
+ }
- return proposals.toArray(new ICompletionProposal[proposals.size()]);
+ /**
+ * Returns true if the given word starts with the given prefix. The comparison is not
+ * case sensitive.
+ *
+ * @param word the word to test
+ * @param prefix the prefix the word should start with
+ * @return true if the given word starts with the given prefix
+ */
+ protected static boolean startsWith(String word, String prefix) {
+ int prefixLength = prefix.length();
+ int wordLength = word.length();
+ if (wordLength < prefixLength) {
+ return false;
+ }
+
+ for (int i = 0; i < prefixLength; i++) {
+ if (Character.toLowerCase(prefix.charAt(i))
+ != Character.toLowerCase(word.charAt(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * This method performs a prefix match for the given word and prefix, with a couple of
+ * Android code completion specific twists:
+ * <ol>
+ * <li> The match is not case sensitive, so {word="fOo",prefix="FoO"} is a match.
+ * <li>If the word to be matched has a namespace prefix, the typed prefix doesn't have
+ * to match it. So {word="android:foo", prefix="foo"} is a match.
+ * <li>If the attribute name part starts with "layout_" it can be omitted. So
+ * {word="android:layout_marginTop",prefix="margin"} is a match, as is
+ * {word="android:layout_marginTop",prefix="android:margin"}.
+ * </ol>
+ *
+ * @param word the full word to be matched, including namespace if any
+ * @param prefix the prefix to check
+ * @param nsPrefix the namespace prefix (android: or local definition of android
+ * namespace prefix)
+ * @return true if the prefix matches for code completion
+ */
+ protected static boolean nameStartsWith(String word, String prefix, String nsPrefix) {
+ if (nsPrefix == null) {
+ nsPrefix = ""; //$NON-NLS-1$
+ }
+
+ int wordStart = nsPrefix.length();
+ int prefixStart = 0;
+
+ if (startsWith(prefix, nsPrefix)) {
+ // Already matches up through the namespace prefix:
+ prefixStart = wordStart;
+ } else if (startsWith(nsPrefix, prefix)) {
+ return true;
+ }
+
+ int prefixLength = prefix.length();
+ int wordLength = word.length();
+
+ if (wordLength - wordStart < prefixLength - prefixStart) {
+ return false;
+ }
+
+ boolean matches = true;
+ for (int i = prefixStart, j = wordStart; i < prefixLength; i++, j++) {
+ char c1 = Character.toLowerCase(prefix.charAt(i));
+ char c2 = Character.toLowerCase(word.charAt(j));
+ if (c1 != c2) {
+ matches = false;
+ break;
+ }
+ }
+
+ if (!matches && word.startsWith(ATTR_LAYOUT_PREFIX, wordStart)
+ && !prefix.startsWith(ATTR_LAYOUT_PREFIX, prefixStart)) {
+ wordStart += ATTR_LAYOUT_PREFIX.length();
+
+ if (wordLength - wordStart < prefixLength - prefixStart) {
+ return false;
+ }
+
+ for (int i = prefixStart, j = wordStart; i < prefixLength; i++, j++) {
+ char c1 = Character.toLowerCase(prefix.charAt(i));
+ char c2 = Character.toLowerCase(word.charAt(j));
+ if (c1 != c2) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return matches;
}
/**
@@ -521,7 +830,8 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
* or if one of the attributes is a TextValueDescriptor.
*
* @param descriptor An ElementDescriptor or an AttributeDescriptor
- * @return True if the descriptor is an ElementDescriptor that can have children or a text value
+ * @return True if the descriptor is an ElementDescriptor that can have children or a text
+ * value
*/
private boolean elementCanHaveChildren(Object descriptor) {
if (descriptor instanceof ElementDescriptor) {
@@ -529,8 +839,8 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
if (desc.hasChildren()) {
return true;
}
- for (AttributeDescriptor attr_desc : desc.getAttributes()) {
- if (attr_desc instanceof TextValueDescriptor) {
+ for (AttributeDescriptor attrDesc : desc.getAttributes()) {
+ if (attrDesc instanceof TextValueDescriptor) {
return true;
}
}
@@ -637,25 +947,53 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
}
/**
+ * Search forward and find the first non-space character and return it. Returns 0 if no
+ * such character was found.
+ */
+ private char nextNonspaceChar(ITextViewer viewer, int offset) {
+ IDocument document = viewer.getDocument();
+ int length = document.getLength();
+ for (; offset < length; offset++) {
+ try {
+ char c = document.getChar(offset);
+ if (!Character.isWhitespace(c)) {
+ return c;
+ }
+ } catch (BadLocationException e) {
+ return 0;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
* Information about the current edit of an attribute as reported by parseAttributeInfo.
*/
- private class AttribInfo {
+ protected static class AttribInfo {
+ public AttribInfo() {
+ }
+
/** True if the cursor is located in an attribute's value, false if in an attribute name */
public boolean isInValue = false;
/** The attribute name. Null when not set. */
public String name = null;
- /** The attribute value. Null when not set. The value *may* start with a quote
- * (' or "), in which case we know we don't need to quote the string for the user */
- public String value = null;
+ /** The attribute value top the left of the cursor. Null when not set. The value
+ * *may* start with a quote (' or "), in which case we know we don't need to quote
+ * the string for the user */
+ public String valuePrefix = null;
/** String typed by the user so far (i.e. right before requesting code completion),
* which will be corrected if we find a possible completion for an attribute value.
* See the long comment in getChoicesForAttribute(). */
public String correctedPrefix = null;
/** Non-zero if an attribute value need a start/end tag (i.e. quotes or brackets) */
public char needTag = 0;
+ /** Number of characters to replace after the prefix */
+ public int replaceLength = 0;
+ /** Should the cursor advance through the end tag when inserted? */
+ public boolean skipEndTag = false;
}
-
/**
* Try to guess if the cursor is editing an element's name or an attribute following an
* element. If it's an attribute, try to find if an attribute name is being defined or
@@ -669,16 +1007,24 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
* @return An AttribInfo describing which attribute is being edited or null if the cursor is
* not editing an attribute (in which case it must be an element's name).
*/
- private AttribInfo parseAttributeInfo(ITextViewer viewer, int offset) {
+ private AttribInfo parseAttributeInfo(ITextViewer viewer, int offset, int prefixStartOffset) {
AttribInfo info = new AttribInfo();
+ int originalOffset = offset;
IDocument document = viewer.getDocument();
int n = document.getLength();
if (offset <= n) {
try {
+ // Look to the right to make sure we aren't sitting on the boundary of the
+ // beginning of a new element with whitespace before it
+ if (offset < n && document.getChar(offset) == '<') {
+ return null;
+ }
+
n = offset;
for (;offset > 0; --offset) {
char ch = document.getChar(offset - 1);
+ if (ch == '>') break;
if (ch == '<') break;
}
@@ -711,6 +1057,12 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
text = sFirstAttribute.matcher(temp).replaceFirst(""); //$NON-NLS-1$
} while(!temp.equals(text));
+ IRegion lineInfo = document.getLineInformationOfOffset(originalOffset);
+ int lineStart = lineInfo.getOffset();
+ String line = document.get(lineStart, lineInfo.getLength());
+ int cursorColumn = originalOffset - lineStart;
+ int prefixLength = originalOffset - prefixStartOffset;
+
// Now we're left with 3 cases:
// - nothing: either there is no attribute definition or the cursor located after
// a completed attribute definition.
@@ -718,14 +1070,89 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
// merged with the previous one.
// - string with an = sign, optionally followed by a quote (' or "): the user is
// writing the value of the attribute.
- int pos_equal = text.indexOf('=');
- if (pos_equal == -1) {
+ int posEqual = text.indexOf('=');
+ if (posEqual == -1) {
info.isInValue = false;
info.name = text.trim();
+
+ // info.name is currently just the prefix of the attribute name.
+ // Look at the text buffer to find the complete name (since we need
+ // to know its bounds in order to replace it when a different attribute
+ // that matches this prefix is chosen)
+ int nameStart = cursorColumn;
+ for (int nameEnd = nameStart; nameEnd < line.length(); nameEnd++) {
+ char c = line.charAt(nameEnd);
+ if (!(Character.isLetter(c) || c == ':' || c == '_')) {
+ String nameSuffix = line.substring(nameStart, nameEnd);
+ info.name = text.trim() + nameSuffix;
+ break;
+ }
+ }
+
+ info.replaceLength = info.name.length() - prefixLength;
+
+ if (info.name.length() == 0 && originalOffset > 0) {
+ // Ensure that attribute names are properly separated
+ char prevChar = extractChar(viewer, originalOffset - 1);
+ if (prevChar == '"' || prevChar == '\'') {
+ // Ensure that the attribute is properly separated from the
+ // previous element
+ info.needTag = ' ';
+ }
+ }
+ info.skipEndTag = false;
} else {
info.isInValue = true;
- info.name = text.substring(0, pos_equal).trim();
- info.value = text.substring(pos_equal + 1).trim();
+ info.name = text.substring(0, posEqual).trim();
+ info.valuePrefix = text.substring(posEqual + 1);
+
+ char quoteChar = '"'; // Does " or ' surround the XML value?
+ for (int i = posEqual + 1; i < text.length(); i++) {
+ if (!Character.isWhitespace(text.charAt(i))) {
+ quoteChar = text.charAt(i);
+ break;
+ }
+ }
+
+ // Must compute the complete value
+ int valueStart = cursorColumn;
+ int valueEnd = valueStart;
+ for (; valueEnd < line.length(); valueEnd++) {
+ char c = line.charAt(valueEnd);
+ if (c == quoteChar) {
+ // Make sure this isn't the *opening* quote of the value,
+ // which is the case if we invoke code completion with the
+ // caret between the = and the opening quote; in that case
+ // we consider it value completion, and offer items including
+ // the quotes, but we shouldn't bail here thinking we have found
+ // the end of the value.
+ // Look backwards to make sure we find another " before
+ // we find a =
+ boolean isFirst = false;
+ for (int j = valueEnd - 1; j >= 0; j--) {
+ char pc = line.charAt(j);
+ if (pc == '=') {
+ isFirst = true;
+ break;
+ } else if (pc == quoteChar) {
+ valueStart = j;
+ break;
+ }
+ }
+ if (!isFirst) {
+ info.skipEndTag = true;
+ break;
+ }
+ }
+ }
+ int valueEndOffset = valueEnd + lineStart;
+ info.replaceLength = valueEndOffset - (prefixStartOffset + prefixLength);
+ // Is the caret to the left of the value quote? If so, include it in
+ // the replace length.
+ int valueStartOffset = valueStart + lineStart;
+ if (valueStartOffset == prefixStartOffset && valueEnd > valueStart) {
+ info.replaceLength++;
+ }
}
return info;
} catch (BadLocationException e) {
@@ -736,12 +1163,9 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
return null;
}
-
- /**
- * Returns the XML DOM node corresponding to the given offset of the given document.
- */
- public static Node getNode(ITextViewer viewer, int offset) {
- return AndroidXmlEditor.getNode(viewer.getDocument(), offset);
+ /** Returns the root descriptor id to use */
+ protected int getRootDescriptorId() {
+ return mDescriptorId;
}
/**
@@ -751,7 +1175,8 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
if (mRootDescriptor == null) {
AndroidTargetData data = mEditor.getTargetData();
if (data != null) {
- IDescriptorProvider descriptorProvider = data.getDescriptorProvider(mDescriptorId);
+ IDescriptorProvider descriptorProvider =
+ data.getDescriptorProvider(getRootDescriptorId());
if (descriptorProvider != null) {
mRootDescriptor = new ElementDescriptor("", //$NON-NLS-1$
@@ -764,26 +1189,131 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
}
/**
- * Returns the active {@link AndroidXmlEditor} matching this source viewer.
+ * Fixed list of dimension units, along with user documentation, for use by
+ * {@link #completeSuffix}.
*/
- private AndroidXmlEditor getAndroidXmlEditor(ITextViewer viewer) {
- IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
- if (wwin != null) {
- IWorkbenchPage page = wwin.getActivePage();
- if (page != null) {
- IEditorPart editor = page.getActiveEditor();
- if (editor instanceof AndroidXmlEditor) {
- ISourceViewer ssviewer = ((AndroidXmlEditor) editor).getStructuredSourceViewer();
- if (ssviewer == viewer) {
- return (AndroidXmlEditor) editor;
+ private static final String[] sDimensionUnits = new String[] {
+ "dp", //$NON-NLS-1$
+ "<b>Density-independent Pixels</b> - an abstract unit that is based on the physical "
+ + "density of the screen.",
+
+ "sp", //$NON-NLS-1$
+ "<b>Scale-independent Pixels</b> - this is like the dp unit, but it is also scaled by "
+ + "the user's font size preference.",
+
+ "pt", //$NON-NLS-1$
+ "<b>Points</b> - 1/72 of an inch based on the physical size of the screen.",
+
+ "mm", //$NON-NLS-1$
+ "<b>Millimeters</b> - based on the physical size of the screen.",
+
+ "in", //$NON-NLS-1$
+ "<b>Inches</b> - based on the physical size of the screen.",
+
+ "px", //$NON-NLS-1$
+ "<b>Pixels</b> - corresponds to actual pixels on the screen. Not recommended.",
+ };
+
+ /**
+ * Fixed list of fractional units, along with user documentation, for use by
+ * {@link #completeSuffix}
+ */
+ private static final String[] sFractionUnits = new String[] {
+ "%", //$NON-NLS-1$
+ "<b>Fraction</b> - a percentage of the base size",
+
+ "%p", //$NON-NLS-1$
+ "<b>Fraction</b> - a percentage relative to parent container",
+ };
+
+ /**
+ * Completes suffixes for applicable types (like dimensions and fractions) such that
+ * after a dimension number you get completion on unit types like "px".
+ */
+ private Object[] completeSuffix(Object[] choices, String value, UiAttributeNode currAttrNode) {
+ IAttributeInfo attributeInfo = currAttrNode.getDescriptor().getAttributeInfo();
+ Format[] formats = attributeInfo.getFormats();
+ List<Object> suffixes = new ArrayList<Object>();
+
+ if (value.length() > 0 && Character.isDigit(value.charAt(0))) {
+ boolean hasDimension = Format.DIMENSION.in(formats);
+ boolean hasFraction = Format.FRACTION.in(formats);
+
+ if (hasDimension || hasFraction) {
+ // Split up the value into a numeric part (the prefix) and the
+ // unit part (the suffix)
+ int suffixBegin = 0;
+ for (; suffixBegin < value.length(); suffixBegin++) {
+ if (!Character.isDigit(value.charAt(suffixBegin))) {
+ break;
+ }
+ }
+ String number = value.substring(0, suffixBegin);
+ String suffix = value.substring(suffixBegin);
+
+ // Add in the matching dimension and/or fraction units, if any
+ if (hasDimension) {
+ // Each item has two entries in the array of strings: the first odd numbered
+ // ones are the unit names and the second even numbered ones are the
+ // corresponding descriptions.
+ for (int i = 0; i < sDimensionUnits.length; i += 2) {
+ String unit = sDimensionUnits[i];
+ if (startsWith(unit, suffix)) {
+ String description = sDimensionUnits[i + 1];
+ suffixes.add(Pair.of(number + unit, description));
+ }
+ }
+
+ // Allow "dip" completion but don't offer it ("dp" is preferred)
+ if (startsWith(suffix, "di") || startsWith(suffix, "dip")) { //$NON-NLS-1$ //$NON-NLS-2$
+ suffixes.add(Pair.of(number + "dip", "Alternative name for \"dp\"")); //$NON-NLS-1$
+ }
+ }
+ if (hasFraction) {
+ for (int i = 0; i < sFractionUnits.length; i += 2) {
+ String unit = sFractionUnits[i];
+ if (startsWith(unit, suffix)) {
+ String description = sFractionUnits[i + 1];
+ suffixes.add(Pair.of(number + unit, description));
+ }
}
}
}
}
- return null;
- }
-
+ boolean hasFlag = Format.FLAG.in(formats);
+ if (hasFlag) {
+ boolean isDone = false;
+ String[] flagValues = attributeInfo.getFlagValues();
+ for (String flagValue : flagValues) {
+ if (flagValue.equals(value)) {
+ isDone = true;
+ break;
+ }
+ }
+ if (isDone) {
+ // Add in all the new values with a separator of |
+ String currentValue = currAttrNode.getCurrentValue();
+ for (String flagValue : flagValues) {
+ if (currentValue == null || !currentValue.contains(flagValue)) {
+ suffixes.add(value + '|' + flagValue);
+ }
+ }
+ }
+ }
+ if (suffixes.size() > 0) {
+ // Merge previously added choices (from attribute enums etc) with the new matches
+ List<Object> all = new ArrayList<Object>();
+ if (choices != null) {
+ for (Object s : choices) {
+ all.add(s);
+ }
+ }
+ all.addAll(suffixes);
+ choices = all.toArray();
+ }
+ return choices;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidSourceViewerConfig.java
index 38d8844..5a38fec 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidSourceViewerConfig.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidSourceViewerConfig.java
@@ -18,23 +18,28 @@ package com.android.ide.eclipse.adt.internal.editors;
import org.eclipse.jface.text.IAutoEditStrategy;
-import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContentAssistant;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.text.formatter.IContentFormatter;
import org.eclipse.jface.text.source.ISourceViewer;
-import org.eclipse.jface.viewers.IInputProvider;
import org.eclipse.wst.sse.core.text.IStructuredPartitions;
import org.eclipse.wst.xml.core.text.IXMLPartitions;
import org.eclipse.wst.xml.ui.StructuredTextViewerConfigurationXML;
+import org.eclipse.wst.xml.ui.internal.contentassist.XMLContentAssistProcessor;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
/**
* Base Source Viewer Configuration for Android resources.
*/
+@SuppressWarnings("restriction") // XMLContentAssistProcessor
public class AndroidSourceViewerConfig extends StructuredTextViewerConfigurationXML {
/** Content Assist Processor to use for all handled partitions. */
@@ -67,17 +72,6 @@ public class AndroidSourceViewerConfig extends StructuredTextViewerConfiguration
if (partitionType == IStructuredPartitions.UNKNOWN_PARTITION ||
partitionType == IStructuredPartitions.DEFAULT_PARTITION ||
partitionType == IXMLPartitions.XML_DEFAULT) {
- if (sourceViewer instanceof IInputProvider) {
- IInputProvider input = (IInputProvider) sourceViewer;
- Object a = input.getInput();
- if (a != null)
- a.toString();
- }
-
- IDocument doc = sourceViewer.getDocument();
- if (doc != null)
- doc.toString();
-
processors.add(mProcessor);
}
@@ -85,7 +79,13 @@ public class AndroidSourceViewerConfig extends StructuredTextViewerConfiguration
partitionType);
if (others != null && others.length > 0) {
for (IContentAssistProcessor p : others) {
- processors.add(p);
+ // Builtin Eclipse WTP code completion assistant? If so,
+ // wrap it with our own filter which hides some unwanted completions.
+ if (p instanceof XMLContentAssistProcessor) {
+ processors.add(new FilteringContentAssistProcessor(p));
+ } else {
+ processors.add(p);
+ }
}
}
@@ -125,4 +125,70 @@ public class AndroidSourceViewerConfig extends StructuredTextViewerConfiguration
return targets;
}
+ /**
+ * A delegating {@link IContentAssistProcessor} whose purpose is to filter out some
+ * default Eclipse XML completions which are distracting in Android XML files
+ */
+ private class FilteringContentAssistProcessor implements IContentAssistProcessor {
+ private IContentAssistProcessor mDelegate;
+
+ public FilteringContentAssistProcessor(IContentAssistProcessor delegate) {
+ super();
+ mDelegate = delegate;
+ }
+
+ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
+ ICompletionProposal[] result = mDelegate.computeCompletionProposals(viewer, offset);
+ if (result == null) {
+ return null;
+ }
+
+ List<ICompletionProposal> proposals =
+ new ArrayList<ICompletionProposal>(result.length);
+ for (ICompletionProposal proposal : result) {
+ String replacement = proposal.getDisplayString();
+ if (replacement.charAt(0) == '"' &&
+ replacement.charAt(replacement.length() - 1) == '"') {
+ // Filter out attribute values. In Android XML files (where there is no DTD
+ // etc) the default Eclipse XML code completion simply provides the
+ // existing value as a completion. This is often misleading, since if you
+ // for example have a typo, completion will show your current (wrong)
+ // value as a valid completion.
+ } else if (replacement.contains("Namespace") //$NON-NLS-1$
+ || replacement.startsWith("XSL ") //$NON-NLS-1$
+ || replacement.contains("Schema")) { //$NON-NLS-1$
+ // Eclipse adds in a number of namespace and schema related completions which
+ // are not usually applicable in our files.
+ } else {
+ proposals.add(proposal);
+ }
+ }
+
+ if (proposals.size() == result.length) {
+ return result;
+ } else {
+ return proposals.toArray(new ICompletionProposal[proposals.size()]);
+ }
+ }
+
+ public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
+ return mDelegate.computeContextInformation(viewer, offset);
+ }
+
+ public char[] getCompletionProposalAutoActivationCharacters() {
+ return mDelegate.getCompletionProposalAutoActivationCharacters();
+ }
+
+ public char[] getContextInformationAutoActivationCharacters() {
+ return mDelegate.getContextInformationAutoActivationCharacters();
+ }
+
+ public IContextInformationValidator getContextInformationValidator() {
+ return mDelegate.getContextInformationValidator();
+ }
+
+ public String getErrorMessage() {
+ return mDelegate.getErrorMessage();
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
index 89edee4..bd12e27 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
@@ -40,6 +40,7 @@ import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Display;
@@ -49,7 +50,9 @@ import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.forms.IManagedForm;
@@ -89,6 +92,9 @@ import java.net.URL;
@SuppressWarnings("restriction") // Uses XML model, which has no non-restricted replacement yet
public abstract class AndroidXmlEditor extends FormEditor implements IResourceChangeListener {
+ /** Icon used for the XML source page. */
+ public static final String ICON_XML_PAGE = "editor_page_source"; //$NON-NLS-1$
+
/** Preference name for the current page of this file */
private static final String PREF_CURRENT_PAGE = "_current_page"; //$NON-NLS-1$
@@ -604,12 +610,53 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
*/
private void createTextEditor() {
try {
+ if (AdtPlugin.DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(
+ IStatus.ERROR,
+ "%s.createTextEditor: input=%s %s",
+ this.getClass(),
+ getEditorInput() == null ? "null" : getEditorInput().getClass(),
+ getEditorInput() == null ? "null" : getEditorInput().toString()
+ );
+
+ org.eclipse.core.runtime.IAdaptable adaptable= getEditorInput();
+ IFile file1 = (IFile)adaptable.getAdapter(IFile.class);
+ org.eclipse.core.runtime.IPath location= file1.getFullPath();
+ org.eclipse.core.resources.IWorkspaceRoot workspaceRoot= ResourcesPlugin.getWorkspace().getRoot();
+ IFile file2 = workspaceRoot.getFile(location);
+
+ try {
+ org.eclipse.core.runtime.content.IContentDescription desc = file2.getContentDescription();
+ org.eclipse.core.runtime.content.IContentType type = desc.getContentType();
+
+ AdtPlugin.log(IStatus.ERROR,
+ "file %s description %s %s; contentType %s %s",
+ file2,
+ desc == null ? "null" : desc.getClass(),
+ desc == null ? "null" : desc.toString(),
+ type == null ? "null" : type.getClass(),
+ type == null ? "null" : type.toString());
+
+ } catch (CoreException e) {
+ e.printStackTrace();
+ }
+ }
+
mTextEditor = new StructuredTextEditor();
int index = addPage(mTextEditor, getEditorInput());
mTextPageIndex = index;
setPageText(index, mTextEditor.getTitle());
setPageImage(index,
- IconFactory.getInstance().getIcon("editor_page_source")); //$NON-NLS-1$
+ IconFactory.getInstance().getIcon(ICON_XML_PAGE));
+
+ if (AdtPlugin.DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.ERROR, "Found document class: %1$s, file=%2$s",
+ mTextEditor.getTextViewer().getDocument() != null ?
+ mTextEditor.getTextViewer().getDocument().getClass() :
+ "null",
+ getEditorInput()
+ );
+ }
if (!(mTextEditor.getTextViewer().getDocument() instanceof IStructuredDocument)) {
Status status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
@@ -661,7 +708,7 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
* Returns the {@link IStructuredDocument} used by the StructuredTextEditor (aka Source
* Editor) or null if not available.
*/
- public final IStructuredDocument getStructuredDocument() {
+ public IStructuredDocument getStructuredDocument() {
if (mTextEditor != null && mTextEditor.getTextViewer() != null) {
return (IStructuredDocument) mTextEditor.getTextViewer().getDocument();
}
@@ -669,39 +716,6 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
}
/**
- * Returns the XML DOM node corresponding to the given offset of the given
- * document.
- *
- * @param document The document to look in
- * @param offset The offset to look up the node for
- * @return The node containing the offset, or null
- */
- @SuppressWarnings("restriction") // No replacement for restricted XML model yet
- public static Node getNode(IDocument document, int offset) {
- Node node = null;
- IModelManager modelManager = StructuredModelManager.getModelManager();
- if (modelManager == null) {
- return null;
- }
- try {
- IStructuredModel model = modelManager.getExistingModelForRead(document);
- if (model != null) {
- try {
- for (; offset >= 0 && node == null; --offset) {
- node = (Node) model.getIndexedRegion(offset);
- }
- } finally {
- model.releaseFromRead();
- }
- }
- } catch (Exception e) {
- // Ignore exceptions.
- }
-
- return node;
- }
-
- /**
* Returns a version of the model that has been shared for read.
* <p/>
* Callers <em>must</em> call model.releaseFromRead() when done, typically
@@ -714,7 +728,7 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
*
* @return The model for the XML document or null if cannot be obtained from the editor
*/
- public final IStructuredModel getModelForRead() {
+ public IStructuredModel getModelForRead() {
IStructuredDocument document = getStructuredDocument();
if (document != null) {
IModelManager mm = StructuredModelManager.getModelManager();
@@ -743,7 +757,6 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
* @return The model for the XML document or null if cannot be obtained from the editor
*/
private IStructuredModel getModelForEdit() {
-
IStructuredDocument document = getStructuredDocument();
if (document != null) {
IModelManager mm = StructuredModelManager.getModelManager();
@@ -1139,8 +1152,7 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
* @return The indent-string of the given node, or "" if the indentation for some reason could
* not be computed.
*/
- public static String getIndent(IStructuredDocument document, Node xmlNode) {
- assert xmlNode.getNodeType() == Node.ELEMENT_NODE;
+ public static String getIndent(IDocument document, Node xmlNode) {
if (xmlNode instanceof IndexedRegion) {
IndexedRegion region = (IndexedRegion)xmlNode;
int startOffset = region.getStartOffset();
@@ -1158,7 +1170,7 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
* @return The indent-string of the given node, or "" if the indentation for some
* reason could not be computed.
*/
- public static String getIndentAtOffset(IStructuredDocument document, int offset) {
+ public static String getIndentAtOffset(IDocument document, int offset) {
try {
IRegion lineInformation = document.getLineInformationOfOffset(offset);
if (lineInformation != null) {
@@ -1186,6 +1198,32 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
}
/**
+ * Returns the active {@link AndroidXmlEditor}, provided it matches the given source
+ * viewer
+ *
+ * @param viewer the source viewer to ensure the active editor is associated with
+ * @return the active editor provided it matches the given source viewer
+ */
+ public static AndroidXmlEditor getAndroidXmlEditor(ITextViewer viewer) {
+ IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (wwin != null) {
+ IWorkbenchPage page = wwin.getActivePage();
+ if (page != null) {
+ IEditorPart editor = page.getActiveEditor();
+ if (editor instanceof AndroidXmlEditor) {
+ ISourceViewer ssviewer =
+ ((AndroidXmlEditor) editor).getStructuredSourceViewer();
+ if (ssviewer == viewer) {
+ return (AndroidXmlEditor) editor;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
* Listen to changes in the underlying XML model in the structured editor.
*/
private class XmlModelStateListener implements IModelStateListener {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimDescriptors.java
new file mode 100644
index 0000000..dabab87
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimDescriptors.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 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.editors.animator;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
+
+import com.android.ide.common.resources.platform.DeclareStyleableInfo;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
+import com.android.sdklib.SdkConstants;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Descriptors for the res/anim resources */
+public class AnimDescriptors implements IDescriptorProvider {
+ /** The root element descriptor */
+ private ElementDescriptor mDescriptor;
+ /** The root element descriptors */
+ private ElementDescriptor[] mRootDescriptors;
+ private Map<String, ElementDescriptor> nameToDescriptor;
+
+ /** @return the root descriptor. */
+ public ElementDescriptor getDescriptor() {
+ if (mDescriptor == null) {
+ mDescriptor = new ElementDescriptor("", getRootElementDescriptors()); //$NON-NLS-1$
+ }
+
+ return mDescriptor;
+ }
+
+ public ElementDescriptor[] getRootElementDescriptors() {
+ return mRootDescriptors;
+ }
+
+ public ElementDescriptor getElementDescriptor(String mRootTag) {
+ if (nameToDescriptor == null) {
+ nameToDescriptor = new HashMap<String, ElementDescriptor>();
+ for (ElementDescriptor descriptor : getRootElementDescriptors()) {
+ nameToDescriptor.put(descriptor.getXmlName(), descriptor);
+ }
+ }
+
+ ElementDescriptor descriptor = nameToDescriptor.get(mRootTag);
+ if (descriptor == null) {
+ descriptor = getDescriptor();
+ }
+ return descriptor;
+ }
+
+ public synchronized void updateDescriptors(Map<String, DeclareStyleableInfo> styleMap) {
+ if (styleMap == null) {
+ return;
+ }
+
+ XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_NAME,
+ SdkConstants.NS_RESOURCES);
+
+ List<ElementDescriptor> descriptors = new ArrayList<ElementDescriptor>();
+
+ String sdkUrl =
+ "http://developer.android.com/guide/topics/graphics/view-animation.html"; //$NON-NLS-1$
+ ElementDescriptor set = AnimatorDescriptors.addElement(descriptors, styleMap,
+ "set", "Set", "AnimationSet", "Animation", //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$
+ "A container that holds other animation elements (<alpha>, <scale>, "
+ + "<translate>, <rotate>) or other <set> elements. ",
+ sdkUrl,
+ xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "alpha", "Alpha", "AlphaAnimation", "Animation", //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$
+ "A fade-in or fade-out animation.",
+ sdkUrl,
+ xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "scale", "Scale", "ScaleAnimation", "Animation", //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$
+ "A resizing animation. You can specify the center point of the image from "
+ + "which it grows outward (or inward) by specifying pivotX and pivotY. "
+ + "For example, if these values are 0, 0 (top-left corner), all growth "
+ + "will be down and to the right.",
+ sdkUrl,
+ xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "rotate", "Rotate", "RotateAnimation", "Animation", //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$
+ "A rotation animation.",
+ sdkUrl,
+ xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "translate", "Translate", "TranslateAnimation", "Animation", //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$
+ "A vertical and/or horizontal motion. Supports the following attributes in "
+ + "any of the following three formats: values from -100 to 100 ending "
+ + "with \"%\", indicating a percentage relative to itself; values from "
+ + "-100 to 100 ending in \"%p\", indicating a percentage relative to its "
+ + "parent; a float value with no suffix, indicating an absolute value.",
+ sdkUrl,
+ xmlns, null, true /*mandatory*/);
+
+ mRootDescriptors = descriptors.toArray(new ElementDescriptor[descriptors.size()]);
+
+ // Allow <set> to nest the others (and other sets)
+ if (set != null) {
+ set.setChildren(mRootDescriptors);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationContentAssist.java
new file mode 100644
index 0000000..ee0c290
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationContentAssist.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.animator;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.common.api.IAttributeInfo.Format;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+import com.android.util.Pair;
+
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.w3c.dom.Node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Content Assist Processor for /res/drawable XML files
+ */
+@VisibleForTesting
+public final class AnimationContentAssist extends AndroidContentAssist {
+ private static final String OBJECT_ANIMATOR = "objectAnimator"; //$NON-NLS-1$
+ private static final String PROPERTY_NAME = "propertyName"; //$NON-NLS-1$
+ private static final String INTERPOLATOR_PROPERTY_NAME = "interpolator"; //$NON-NLS-1$
+ private static final String INTERPOLATOR_NAME_SUFFIX = "_interpolator"; //$NON-NLS-1$
+
+ public AnimationContentAssist() {
+ super(AndroidTargetData.DESCRIPTOR_ANIMATOR);
+ }
+
+ @Override
+ protected int getRootDescriptorId() {
+ String folderName = mEditor.getInputFile().getParent().getName();
+ ResourceFolderType folderType = ResourceFolderType.getFolderType(folderName);
+ if (folderType == ResourceFolderType.ANIM) {
+ return AndroidTargetData.DESCRIPTOR_ANIM;
+ } else {
+ return AndroidTargetData.DESCRIPTOR_ANIMATOR;
+ }
+ }
+
+ @Override
+ protected void computeAttributeValues(List<ICompletionProposal> proposals, int offset,
+ String parentTagName, String attributeName, Node node, String wordPrefix,
+ boolean skipEndTag, int replaceLength) {
+
+ // Add value completion for the interpolator and propertyName attributes
+
+ if (attributeName.endsWith(INTERPOLATOR_PROPERTY_NAME)) {
+ if (!wordPrefix.startsWith("@android:anim/")) { //$NON-NLS-1$
+ // List all framework interpolators with full path first
+ AndroidTargetData data = mEditor.getTargetData();
+ ResourceRepository repository = data.getFrameworkResources();
+ List<String> interpolators = new ArrayList<String>();
+ String base = '@' + ANDROID_PKG + ':' + ResourceType.ANIM.getName() + '/';
+ for (ResourceItem item : repository.getResourceItemsOfType(ResourceType.ANIM)) {
+ String name = item.getName();
+ if (name.endsWith(INTERPOLATOR_NAME_SUFFIX)) {
+ interpolators.add(base + item.getName());
+ }
+ }
+ addMatchingProposals(proposals, interpolators.toArray(), offset, node, wordPrefix,
+ (char) 0 /* needTag */, true /* isAttribute */, false /* isNew */,
+ skipEndTag /* skipEndTag */, replaceLength);
+ }
+
+
+ super.computeAttributeValues(proposals, offset, parentTagName, attributeName, node,
+ wordPrefix, skipEndTag, replaceLength);
+ } else if (parentTagName.equals(OBJECT_ANIMATOR)
+ && attributeName.endsWith(PROPERTY_NAME)) {
+
+ // Special case: the user is code completing inside
+ // <objectAnimator propertyName="^">
+ // In this case, offer ALL attribute names that make sense for animation
+ // (e.g. all numeric ones)
+
+ String attributePrefix = wordPrefix;
+ if (startsWith(attributePrefix, ANDROID_NS_NAME_PREFIX)) {
+ attributePrefix = attributePrefix.substring(ANDROID_NS_NAME_PREFIX.length());
+ }
+
+ AndroidTargetData data = mEditor.getTargetData();
+ if (data != null) {
+ IDescriptorProvider descriptorProvider =
+ data.getDescriptorProvider(AndroidTargetData.DESCRIPTOR_LAYOUT);
+ if (descriptorProvider != null) {
+ ElementDescriptor[] rootElementDescriptors =
+ descriptorProvider.getRootElementDescriptors();
+ Map<String, AttributeDescriptor> matches =
+ new HashMap<String, AttributeDescriptor>(180);
+ for (ElementDescriptor elementDesc : rootElementDescriptors) {
+ for (AttributeDescriptor desc : elementDesc.getAttributes()) {
+ if (desc instanceof SeparatorAttributeDescriptor) {
+ continue;
+ }
+ String name = desc.getXmlLocalName();
+ if (startsWith(name, attributePrefix)) {
+ for (Format f : desc.getAttributeInfo().getFormats()) {
+ if (f == Format.INTEGER || f == Format.FLOAT) {
+ // TODO: Filter out some common properties
+ // that the user probably isn't trying to
+ // animate:
+ // num*, min*, max*, *Index, *Threshold,
+ // *Duration, *Id, *Limit
+
+ matches.put(name, desc);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ List<AttributeDescriptor> sorted =
+ new ArrayList<AttributeDescriptor>(matches.size());
+ sorted.addAll(matches.values());
+ Collections.sort(sorted);
+ // Extract just the name+description pairs, since we don't want to
+ // use the full attribute descriptor (which forces the namespace
+ // prefix to be included)
+ List<Pair<String, String>> pairs =
+ new ArrayList<Pair<String, String>>(sorted.size());
+ for (AttributeDescriptor d : sorted) {
+ pairs.add(Pair.of(d.getXmlLocalName(), d.getAttributeInfo().getJavaDoc()));
+ }
+
+ addMatchingProposals(proposals, pairs.toArray(), offset, node, wordPrefix,
+ (char) 0 /* needTag */, true /* isAttribute */, false /* isNew */,
+ skipEndTag /* skipEndTag */, replaceLength);
+ return;
+ }
+ }
+ } else {
+ super.computeAttributeValues(proposals, offset, parentTagName, attributeName, node,
+ wordPrefix, skipEndTag, replaceLength);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationEditor.java
new file mode 100644
index 0000000..9f61238
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationEditor.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.animator;
+
+import static com.android.ide.eclipse.adt.AdtConstants.EDITORS_NAMESPACE;
+
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.resources.ResourceFolderType;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Editor for /res/animator XML files.
+ */
+@SuppressWarnings("restriction")
+public class AnimationEditor extends AndroidXmlEditor {
+ public static final String ID = EDITORS_NAMESPACE + ".animator.AnimationEditor"; //$NON-NLS-1$
+
+ /** Root node of the UI element hierarchy */
+ private UiElementNode mUiRootNode;
+ /** The tag used at the root */
+ private String mRootTag;
+
+ public AnimationEditor() {
+ super();
+ }
+
+ @Override
+ public UiElementNode getUiRootNode() {
+ return mUiRootNode;
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ return true;
+ }
+
+ @Override
+ protected void createFormPages() {
+ /* Disabled for now; doesn't work quite right
+ try {
+ addPage(new AnimatorTreePage(this));
+ } catch (PartInitException e) {
+ AdtPlugin.log(IStatus.ERROR, "Error creating nested page"); //$NON-NLS-1$
+ AdtPlugin.getDefault().getLog().log(e.getStatus());
+ }
+ */
+ }
+
+ /* (non-java doc)
+ * Change the tab/title name to include the project name.
+ */
+ @Override
+ protected void setInput(IEditorInput input) {
+ super.setInput(input);
+ if (input instanceof FileEditorInput) {
+ FileEditorInput fileInput = (FileEditorInput) input;
+ IFile file = fileInput.getFile();
+ setPartName(String.format("%1$s",
+ file.getName()));
+ }
+ }
+
+ /**
+ * Processes the new XML Model.
+ *
+ * @param xmlDoc The XML document, if available, or null if none exists.
+ */
+ @Override
+ protected void xmlModelChanged(Document xmlDoc) {
+ Element rootElement = xmlDoc.getDocumentElement();
+ if (rootElement != null) {
+ mRootTag = rootElement.getTagName();
+ }
+
+ // create the ui root node on demand.
+ initUiRootNode(false /*force*/);
+
+ if (mRootTag != null
+ && !mRootTag.equals(mUiRootNode.getDescriptor().getXmlLocalName())) {
+ AndroidTargetData data = getTargetData();
+ if (data != null) {
+ ElementDescriptor descriptor;
+ if (getFolderType() == ResourceFolderType.ANIM) {
+ descriptor = data.getAnimDescriptors().getElementDescriptor(mRootTag);
+ } else {
+ descriptor = data.getAnimatorDescriptors().getElementDescriptor(mRootTag);
+ }
+ // Replace top level node now that we know the actual type
+
+ // Disconnect from old
+ mUiRootNode.setEditor(null);
+ mUiRootNode.setXmlDocument(null);
+
+ // Create new
+ mUiRootNode = descriptor.createUiNode();
+ mUiRootNode.setXmlDocument(xmlDoc);
+ mUiRootNode.setEditor(this);
+ }
+ }
+
+ if (mUiRootNode.getDescriptor() instanceof DocumentDescriptor) {
+ mUiRootNode.loadFromXmlNode(xmlDoc);
+ } else {
+ mUiRootNode.loadFromXmlNode(rootElement);
+ }
+
+ super.xmlModelChanged(xmlDoc);
+ }
+
+ @Override
+ protected void initUiRootNode(boolean force) {
+ // The manifest UI node is always created, even if there's no corresponding XML node.
+ if (mUiRootNode == null || force) {
+ ElementDescriptor descriptor;
+ boolean reload = false;
+ AndroidTargetData data = getTargetData();
+ if (data == null) {
+ descriptor = new DocumentDescriptor("temp", null /*children*/);
+ } else {
+ if (getFolderType() == ResourceFolderType.ANIM) {
+ descriptor = data.getAnimDescriptors().getElementDescriptor(mRootTag);
+ } else {
+ descriptor = data.getAnimatorDescriptors().getElementDescriptor(mRootTag);
+ }
+ reload = true;
+ }
+ mUiRootNode = descriptor.createUiNode();
+ mUiRootNode.setEditor(this);
+
+ if (reload) {
+ onDescriptorsChanged();
+ }
+ }
+ }
+
+ private ResourceFolderType getFolderType() {
+ IFile inputFile = getInputFile();
+ if (inputFile != null) {
+ String folderName = inputFile.getParent().getName();
+ return ResourceFolderType.getFolderType(folderName);
+ }
+ return ResourceFolderType.ANIMATOR;
+ }
+
+ private void onDescriptorsChanged() {
+ IStructuredModel model = getModelForRead();
+ if (model != null) {
+ try {
+ Node node = getXmlDocument(model).getDocumentElement();
+ mUiRootNode.reloadFromXmlNode(node);
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IIdResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationSourceViewerConfig.java
index acc4cf2..87a145b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IIdResourceItem.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimationSourceViewerConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources;
+package com.android.ide.eclipse.adt.internal.editors.animator;
-import com.android.resources.ResourceType;
+
+import com.android.ide.eclipse.adt.internal.editors.AndroidSourceViewerConfig;
/**
- * Classes which implements this interface provides a method indicating the state of a resource of
- * type {@link ResourceType#ID}.
+ * Source Viewer Configuration for animation files.
*/
-public interface IIdResourceItem {
- /**
- * Returns whether the ID resource has been declared inline inside another resource XML file.
- */
- public boolean isDeclaredInline();
+public class AnimationSourceViewerConfig extends AndroidSourceViewerConfig {
+
+ public AnimationSourceViewerConfig() {
+ super(new AnimationContentAssist());
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimatorDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimatorDescriptors.java
new file mode 100644
index 0000000..644aebb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/animator/AnimatorDescriptors.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2011 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.editors.animator;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
+
+import com.android.ide.common.resources.platform.DeclareStyleableInfo;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
+import com.android.sdklib.SdkConstants;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Descriptors for /res/animator XML files.
+ */
+public class AnimatorDescriptors implements IDescriptorProvider {
+ /** The root element descriptor */
+ private ElementDescriptor mDescriptor;
+ /** The root element descriptors */
+ private ElementDescriptor[] mRootDescriptors;
+ private Map<String, ElementDescriptor> nameToDescriptor;
+
+ /** @return the root descriptor. */
+ public ElementDescriptor getDescriptor() {
+ if (mDescriptor == null) {
+ mDescriptor = new ElementDescriptor("", getRootElementDescriptors()); //$NON-NLS-1$
+ }
+
+ return mDescriptor;
+ }
+
+ public ElementDescriptor[] getRootElementDescriptors() {
+ return mRootDescriptors;
+ }
+
+ public ElementDescriptor getElementDescriptor(String mRootTag) {
+ if (nameToDescriptor == null) {
+ nameToDescriptor = new HashMap<String, ElementDescriptor>();
+ for (ElementDescriptor descriptor : getRootElementDescriptors()) {
+ nameToDescriptor.put(descriptor.getXmlName(), descriptor);
+ }
+ }
+
+ ElementDescriptor descriptor = nameToDescriptor.get(mRootTag);
+ if (descriptor == null) {
+ descriptor = getDescriptor();
+ }
+ return descriptor;
+ }
+
+ public synchronized void updateDescriptors(Map<String, DeclareStyleableInfo> styleMap) {
+ if (styleMap == null) {
+ return;
+ }
+
+ XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_NAME,
+ SdkConstants.NS_RESOURCES);
+
+ List<ElementDescriptor> descriptors = new ArrayList<ElementDescriptor>();
+
+ String sdkUrl =
+ "http://developer.android.com/guide/topics/graphics/animation.html"; //$NON-NLS-1$
+
+ ElementDescriptor set = addElement(descriptors, styleMap,
+ "set", "Animator Set", "AnimatorSet", null, //$NON-NLS-1$ //$NON-NLS-3$
+ null /* tooltip */, sdkUrl,
+ xmlns, null, true /*mandatory*/);
+
+ ElementDescriptor objectAnimator = addElement(descriptors, styleMap,
+ "objectAnimator", "Object Animator", //$NON-NLS-1$
+ "PropertyAnimator", "Animator", //$NON-NLS-1$ //$NON-NLS-2$
+ null /* tooltip */, sdkUrl,
+ xmlns, null, true /*mandatory*/);
+
+ ElementDescriptor animator = addElement(descriptors, styleMap,
+ "animator", "Animator", "Animator", null, //$NON-NLS-1$ //$NON-NLS-3$
+ null /* tooltip */, sdkUrl,
+ xmlns, null, true /*mandatory*/);
+
+ mRootDescriptors = descriptors.toArray(new ElementDescriptor[descriptors.size()]);
+
+ // Allow arbitrary nesting: the children of all of these element can include
+ // any of the others
+ if (objectAnimator != null) {
+ objectAnimator.setChildren(mRootDescriptors);
+ }
+ if (animator != null) {
+ animator.setChildren(mRootDescriptors);
+ }
+ if (set != null) {
+ set.setChildren(mRootDescriptors);
+ }
+ }
+
+ /**
+ * Looks up the given style, and if found creates a new {@link ElementDescriptor}
+ * corresponding to the style. It can optionally take an extra style to merge in
+ * additional attributes from, and an extra attribute to add in as well. The new
+ * element, if it exists, can also be optionally added into a list.
+ *
+ * @param descriptors an optional list to add the element into, or null
+ * @param styleMap The map style => attributes from the attrs.xml file
+ * @param xmlName the XML tag name to use for the element
+ * @param uiName the UI name to display the element as
+ * @param styleName the name of the style which must exist for this style
+ * @param extraStyle an optional extra style to merge in attributes from, or null
+ * @param tooltip the tooltip or documentation for this element, or null
+ * @param sdkUrl an optional SDK url to display for the element, or null
+ * @param extraAttribute an extra attribute to add to the attributes list, or null
+ * @param childrenElements an array of children allowed by this element, or null
+ * @param mandatory if true, this element is mandatory
+ * @return a newly created element, or null if the style does not exist
+ */
+ public static ElementDescriptor addElement(
+ List<ElementDescriptor> descriptors,
+ Map<String, DeclareStyleableInfo> styleMap,
+ String xmlName, String uiName, String styleName, String extraStyle,
+ String tooltip, String sdkUrl,
+ AttributeDescriptor extraAttribute,
+ ElementDescriptor[] childrenElements,
+ boolean mandatory) {
+ DeclareStyleableInfo style = styleMap.get(styleName);
+ if (style == null) {
+ return null;
+ }
+ ElementDescriptor element = new ElementDescriptor(xmlName, uiName, tooltip, sdkUrl,
+ null, childrenElements, mandatory);
+
+ ArrayList<AttributeDescriptor> descs = new ArrayList<AttributeDescriptor>();
+
+ DescriptorsUtils.appendAttributes(descs,
+ null, // elementName
+ SdkConstants.NS_RESOURCES,
+ style.getAttributes(),
+ null, // requiredAttributes
+ null); // overrides
+ element.setTooltip(style.getJavaDoc());
+
+ if (extraStyle != null) {
+ style = styleMap.get(extraStyle);
+ if (style != null) {
+ DescriptorsUtils.appendAttributes(descs,
+ null, // elementName
+ SdkConstants.NS_RESOURCES,
+ style.getAttributes(),
+ null, // requiredAttributes
+ null); // overrides
+ }
+ }
+
+ if (extraAttribute != null) {
+ descs.add(extraAttribute);
+ }
+
+ element.setAttributes(descs.toArray(new AttributeDescriptor[descs.size()]));
+ if (descriptors != null) {
+ descriptors.add(element);
+ }
+
+ return element;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/binaryxml/BinaryXMLDescriber.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/binaryxml/BinaryXMLDescriber.java
index 846eb44..ba78565 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/binaryxml/BinaryXMLDescriber.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/binaryxml/BinaryXMLDescriber.java
@@ -16,6 +16,9 @@
package com.android.ide.eclipse.adt.internal.editors.binaryxml;
+import com.android.ide.eclipse.adt.AdtPlugin;
+
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.content.IContentDescriber;
import org.eclipse.core.runtime.content.IContentDescription;
@@ -50,20 +53,28 @@ public class BinaryXMLDescriber implements IContentDescriber {
* InputStream, org.eclipse.core.runtime.content.IContentDescription)
*/
public int describe(InputStream contents, IContentDescription description) throws IOException {
+ int status = INVALID;
int length = 8;
byte[] bytes = new byte[length];
- if (contents.read(bytes, 0, length) != length) {
- return INVALID;
+ if (contents.read(bytes, 0, length) == length) {
+ ByteBuffer buf = ByteBuffer.wrap(bytes);
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+ short type = buf.getShort();
+ short headerSize = buf.getShort();
+ int size = buf.getInt(); // chunk size
+ if (AdtPlugin.DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.ERROR, "BinaryXML: type 0x%04x, headerSize 0x%04x, size 0x%08x", type, headerSize, size);
+ }
+ if (type == RES_XML_TYPE && headerSize == RES_XML_HEADER_SIZE) {
+ status = VALID;
+ }
}
- ByteBuffer buf = ByteBuffer.wrap(bytes);
- buf.order(ByteOrder.LITTLE_ENDIAN);
- short type = buf.getShort();
- short headerSize = buf.getShort();
- buf.getInt(); // chunk size
- if (type == RES_XML_TYPE && headerSize == RES_XML_HEADER_SIZE) {
- return VALID;
+ if (AdtPlugin.DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.ERROR, "BinaryXML status: %d (%s)",
+ status,
+ status == VALID ? "VALID" : "INVALID");
}
- return INVALID;
+ return status;
}
/*
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorContentAssist.java
new file mode 100644
index 0000000..1570439
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorContentAssist.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.color;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+
+/**
+ * Content Assist Processor for /res/color XML files
+ */
+@VisibleForTesting
+public final class ColorContentAssist extends AndroidContentAssist {
+ public ColorContentAssist() {
+ super(AndroidTargetData.DESCRIPTOR_COLOR);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorDescriptors.java
new file mode 100644
index 0000000..f6e50a8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorDescriptors.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 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.editors.color;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
+import static com.android.sdklib.SdkConstants.NS_RESOURCES;
+
+import com.android.ide.common.api.IAttributeInfo.Format;
+import com.android.ide.common.resources.platform.AttributeInfo;
+import com.android.ide.common.resources.platform.DeclareStyleableInfo;
+import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ReferenceAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
+import com.android.resources.ResourceType;
+import com.android.sdklib.SdkConstants;
+
+import java.util.Map;
+
+/** Descriptors for /res/color XML files */
+public class ColorDescriptors implements IDescriptorProvider {
+ private static final String SDK_URL =
+ "http://d.android.com/guide/topics/resources/color-list-resource.html"; //$NON-NLS-1$
+
+ /** The root element descriptor */
+ private ElementDescriptor mDescriptor = new ElementDescriptor(
+ "selector", "Selector",
+ "Required. This must be the root element. Contains one or more <item> elements.",
+ SDK_URL,
+ new AttributeDescriptor[] { new XmlnsAttributeDescriptor(ANDROID_NS_NAME,
+ NS_RESOURCES) },
+ null /*children: added later*/, true /*mandatory*/);
+
+ /** @return the root descriptor. */
+ public ElementDescriptor getDescriptor() {
+ if (mDescriptor == null) {
+ mDescriptor = new ElementDescriptor("", getRootElementDescriptors()); //$NON-NLS-1$
+ }
+
+ return mDescriptor;
+ }
+
+ public ElementDescriptor[] getRootElementDescriptors() {
+ return new ElementDescriptor[] { mDescriptor };
+ }
+
+ public synchronized void updateDescriptors(Map<String, DeclareStyleableInfo> styleMap) {
+ if (styleMap == null) {
+ return;
+ }
+
+ // Selector children
+ ElementDescriptor selectorItem = AnimatorDescriptors.addElement(null, styleMap,
+ "item", "Item", "DrawableStates", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "Defines a drawable to use during certain states, as described by "
+ + "its attributes. Must be a child of a <selector> element.",
+ SDK_URL,
+ new ReferenceAttributeDescriptor(
+ ResourceType.COLOR, "color", "color", //$NON-NLS-1$ //$NON-NLS-2$
+ SdkConstants.NS_RESOURCES,
+ "Hexadeximal color. Required. The color is specified with an RGB value and "
+ + "optional alpha channel.\n"
+ + "The value always begins with a pound (#) character and then "
+ + "followed by the Alpha-Red-Green-Blue information in one of "
+ + "the following formats:\n"
+ + "* RGB\n"
+ + "* ARGB\n"
+ + "* RRGGBB\n"
+ + "* AARRGGBB",
+ new AttributeInfo("drawable", new Format[] { Format.COLOR })),
+ null, /* This is wrong -- we can now embed any above drawable
+ (but without xmlns as extra) */
+ false /*mandatory*/);
+
+ if (selectorItem != null) {
+ mDescriptor.setChildren(new ElementDescriptor[] { selectorItem });
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorEditor.java
new file mode 100644
index 0000000..b0e3327
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorEditor.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.color;
+
+import static com.android.ide.eclipse.adt.AdtConstants.EDITORS_NAMESPACE;
+
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Editor for /res/color XML files.
+ */
+@SuppressWarnings("restriction")
+public class ColorEditor extends AndroidXmlEditor {
+ public static final String ID = EDITORS_NAMESPACE + ".color.ColorEditor"; //$NON-NLS-1$
+
+ /** Root node of the UI element hierarchy */
+ private UiElementNode mUiRootNode;
+
+ public ColorEditor() {
+ super();
+ }
+
+ @Override
+ public UiElementNode getUiRootNode() {
+ return mUiRootNode;
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ return true;
+ }
+
+ @Override
+ protected void createFormPages() {
+ /* Disabled for now; doesn't work quite right
+ try {
+ addPage(new ColorTreePage(this));
+ } catch (PartInitException e) {
+ AdtPlugin.log(IStatus.ERROR, "Error creating nested page"); //$NON-NLS-1$
+ AdtPlugin.getDefault().getLog().log(e.getStatus());
+ }
+ */
+ }
+
+ /* (non-java doc)
+ * Change the tab/title name to include the project name.
+ */
+ @Override
+ protected void setInput(IEditorInput input) {
+ super.setInput(input);
+ if (input instanceof FileEditorInput) {
+ FileEditorInput fileInput = (FileEditorInput) input;
+ IFile file = fileInput.getFile();
+ setPartName(String.format("%1$s",
+ file.getName()));
+ }
+ }
+
+ @Override
+ protected void xmlModelChanged(Document xmlDoc) {
+ // create the ui root node on demand.
+ initUiRootNode(false /*force*/);
+
+ Element rootElement = xmlDoc.getDocumentElement();
+ mUiRootNode.loadFromXmlNode(rootElement);
+
+ super.xmlModelChanged(xmlDoc);
+ }
+
+ @Override
+ protected void initUiRootNode(boolean force) {
+ // The manifest UI node is always created, even if there's no corresponding XML node.
+ if (mUiRootNode == null || force) {
+ ElementDescriptor descriptor;
+ AndroidTargetData data = getTargetData();
+ if (data == null) {
+ descriptor = new ColorDescriptors().getDescriptor();
+ } else {
+ descriptor = data.getColorDescriptors().getDescriptor();
+ }
+ mUiRootNode = descriptor.createUiNode();
+ mUiRootNode.setEditor(this);
+ onDescriptorsChanged();
+ }
+ }
+
+ private void onDescriptorsChanged() {
+ IStructuredModel model = getModelForRead();
+ if (model != null) {
+ try {
+ Node node = getXmlDocument(model).getDocumentElement();
+ mUiRootNode.reloadFromXmlNode(node);
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorSourceViewerConfig.java
new file mode 100644
index 0000000..4f12ce5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/color/ColorSourceViewerConfig.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.color;
+
+
+import com.android.ide.eclipse.adt.internal.editors.AndroidSourceViewerConfig;
+
+/**
+ * Source Viewer Configuration that calls in ColorContentAssist.
+ */
+public class ColorSourceViewerConfig extends AndroidSourceViewerConfig {
+
+ public ColorSourceViewerConfig() {
+ super(new ColorContentAssist());
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/AttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/AttributeDescriptor.java
index 222684d..3e4c6d0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/AttributeDescriptor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/AttributeDescriptor.java
@@ -17,7 +17,6 @@
package com.android.ide.eclipse.adt.internal.editors.descriptors;
import com.android.ide.common.api.IAttributeInfo;
-import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
@@ -35,7 +34,9 @@ import org.eclipse.swt.graphics.Image;
* This is an abstract class. Derived classes must implement data description and return
* the correct UiAttributeNode-derived class.
*/
-public abstract class AttributeDescriptor {
+public abstract class AttributeDescriptor implements Comparable<AttributeDescriptor> {
+ public static final String ATTRIBUTE_ICON_FILENAME = "attribute"; //$NON-NLS-1$
+
private final String mXmlLocalName;
private final String mNsUri;
private final IAttributeInfo mAttrInfo;
@@ -53,6 +54,7 @@ public abstract class AttributeDescriptor {
* or attribute separator, all of which do not represent any real attribute.)
*/
public AttributeDescriptor(String xmlLocalName, String nsUri, IAttributeInfo attrInfo) {
+ assert xmlLocalName != null;
mXmlLocalName = xmlLocalName;
mNsUri = nsUri;
mAttrInfo = attrInfo;
@@ -95,18 +97,13 @@ public abstract class AttributeDescriptor {
/**
* Returns an optional icon for the attribute.
- * <p/>
- * By default this tries to return an icon based on the XML name of the attribute.
- * If this fails, it tries to return the default Android logo as defined in the
- * plugin. If all fails, it returns null.
+ * This icon is generic, that is all attribute descriptors have the same icon
+ * no matter what they represent.
*
* @return An icon for this element or null.
*/
- public Image getIcon() {
- IconFactory factory = IconFactory.getInstance();
- Image icon;
- icon = factory.getIcon(getXmlLocalName(), IconFactory.COLOR_RED, IconFactory.SHAPE_CIRCLE);
- return icon != null ? icon : AdtPlugin.getAndroidLogo();
+ public Image getGenericIcon() {
+ return IconFactory.getInstance().getIcon(ATTRIBUTE_ICON_FILENAME);
}
/**
@@ -115,4 +112,9 @@ public abstract class AttributeDescriptor {
* attribute has no user interface.
*/
public abstract UiAttributeNode createUiNode(UiElementNode uiParent);
+
+ // Implements Comparable<AttributeDescriptor>
+ public int compareTo(AttributeDescriptor other) {
+ return mXmlLocalName.compareTo(other.mXmlLocalName);
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/BooleanAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/BooleanAttributeDescriptor.java
index d0806c5..f1def39 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/BooleanAttributeDescriptor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/BooleanAttributeDescriptor.java
@@ -24,11 +24,11 @@ import com.android.ide.eclipse.adt.internal.editors.uimodel.UiListAttributeNode;
* It is displayed by a {@link UiListAttributeNode}.
*/
public class BooleanAttributeDescriptor extends ListAttributeDescriptor {
+ private static final String[] VALUES = new String[] { "true", "false" }; //$NON-NLS-1$ //$NON-NLS-2$
public BooleanAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip, IAttributeInfo attrInfo) {
- super(xmlLocalName, uiName, nsUri, tooltip, attrInfo,
- new String[] { "true", "false" }); //$NON-NLS-1$ //$NON-NLS-2$
+ super(xmlLocalName, uiName, nsUri, tooltip, attrInfo, VALUES);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java
index 817d4cb..c7bc5a1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java
@@ -21,6 +21,7 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_BELOW;
import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
+import static com.android.ide.common.layout.LayoutConstants.EDIT_TEXT;
import static com.android.ide.common.layout.LayoutConstants.EXPANDABLE_LIST_VIEW;
import static com.android.ide.common.layout.LayoutConstants.FQCN_ADAPTER_VIEW;
import static com.android.ide.common.layout.LayoutConstants.GALLERY;
@@ -31,10 +32,11 @@ import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.RELATIVE_LAYOUT;
import static com.android.ide.common.layout.LayoutConstants.VALUE_FILL_PARENT;
import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
+import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.REQUEST_FOCUS;
import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.common.resources.platform.AttributeInfo;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.resources.ResourceType;
@@ -64,7 +66,7 @@ public final class DescriptorsUtils {
* The path in the online documentation for the manifest description.
* <p/>
* This is NOT a complete URL. To be used, it needs to be appended
- * to {@link AndroidConstants#CODESITE_BASE_URL} or to the local SDK
+ * to {@link AdtConstants#CODESITE_BASE_URL} or to the local SDK
* documentation.
*/
public static final String MANIFEST_SDK_URL = "/reference/android/R.styleable.html#"; //$NON-NLS-1$
@@ -357,12 +359,12 @@ public final class DescriptorsUtils {
* @return The capitalized string
*/
public static String capitalize(String str) {
- if (str == null || str.length() < 1 || str.charAt(0) < 'a' || str.charAt(0) > 'z') {
+ if (str == null || str.length() < 1 || Character.isUpperCase(str.charAt(0))) {
return str;
}
StringBuilder sb = new StringBuilder();
- sb.append((char)(str.charAt(0) + 'A' - 'a'));
+ sb.append(Character.toUpperCase(str.charAt(0)));
sb.append(str.substring(1));
return sb.toString();
}
@@ -458,7 +460,7 @@ public final class DescriptorsUtils {
StringBuilder sb = new StringBuilder();
- Image icon = elementDescriptor.getIcon();
+ Image icon = elementDescriptor.getCustomizedIcon();
if (icon != null) {
sb.append("<form><li style=\"image\" value=\"" + //$NON-NLS-1$
IMAGE_KEY + "\">"); //$NON-NLS-1$
@@ -670,6 +672,23 @@ public final class DescriptorsUtils {
}
/**
+ * Returns the basename for the given fully qualified class name. It is okay to pass
+ * a basename to this method which will just be returned back.
+ *
+ * @param fqcn The fully qualified class name to convert
+ * @return the basename of the class name
+ */
+ public static String getBasename(String fqcn) {
+ String name = fqcn;
+ int lastDot = name.lastIndexOf('.');
+ if (lastDot != -1) {
+ name = name.substring(lastDot + 1);
+ }
+
+ return name;
+ }
+
+ /**
* Sets the default layout attributes for the a new UiElementNode.
* <p/>
* Note that ideally the node should already be part of a hierarchy so that its
@@ -677,49 +696,59 @@ public final class DescriptorsUtils {
* <p/>
* This does not override attributes which are not empty.
*/
- public static void setDefaultLayoutAttributes(UiElementNode ui_node, boolean updateLayout) {
+ public static void setDefaultLayoutAttributes(UiElementNode node, boolean updateLayout) {
// if this ui_node is a layout and we're adding it to a document, use match_parent for
// both W/H. Otherwise default to wrap_layout.
- boolean fill = ui_node.getDescriptor().hasChildren() &&
- ui_node.getUiParent() instanceof UiDocumentNode;
- ui_node.setAttributeValue(
+ ElementDescriptor descriptor = node.getDescriptor();
+
+ if (descriptor.getXmlLocalName().equals(REQUEST_FOCUS)) {
+ // Don't add ids etc to <requestFocus>
+ return;
+ }
+
+ boolean fill = descriptor.hasChildren() &&
+ node.getUiParent() instanceof UiDocumentNode;
+ node.setAttributeValue(
ATTR_LAYOUT_WIDTH,
SdkConstants.NS_RESOURCES,
fill ? VALUE_FILL_PARENT : VALUE_WRAP_CONTENT,
false /* override */);
- ui_node.setAttributeValue(
+ node.setAttributeValue(
ATTR_LAYOUT_HEIGHT,
SdkConstants.NS_RESOURCES,
fill ? VALUE_FILL_PARENT : VALUE_WRAP_CONTENT,
false /* override */);
- String widget_id = getFreeWidgetId(ui_node);
- if (widget_id != null) {
- ui_node.setAttributeValue(
+ String freeId = getFreeWidgetId(node);
+ if (freeId != null) {
+ node.setAttributeValue(
ATTR_ID,
SdkConstants.NS_RESOURCES,
- widget_id,
+ freeId,
false /* override */);
}
- String widget_type = ui_node.getDescriptor().getUiName();
- ui_node.setAttributeValue(
+ // Don't set default text value into edit texts - they typically start out blank
+ if (!descriptor.getXmlLocalName().equals(EDIT_TEXT)) {
+ String type = getBasename(descriptor.getUiName());
+ node.setAttributeValue(
ATTR_TEXT,
SdkConstants.NS_RESOURCES,
- widget_type,
+ type,
false /*override*/);
+ }
if (updateLayout) {
- UiElementNode ui_parent = ui_node.getUiParent();
- if (ui_parent != null &&
- ui_parent.getDescriptor().getXmlLocalName().equals(
+ UiElementNode parent = node.getUiParent();
+ if (parent != null &&
+ parent.getDescriptor().getXmlLocalName().equals(
RELATIVE_LAYOUT)) {
- UiElementNode ui_previous = ui_node.getUiPreviousSibling();
- if (ui_previous != null) {
- String id = ui_previous.getAttributeValue(ATTR_ID);
+ UiElementNode previous = node.getUiPreviousSibling();
+ if (previous != null) {
+ String id = previous.getAttributeValue(ATTR_ID);
if (id != null && id.length() > 0) {
id = id.replace("@+", "@"); //$NON-NLS-1$ //$NON-NLS-2$
- ui_node.setAttributeValue(
+ node.setAttributeValue(
ATTR_LAYOUT_BELOW,
SdkConstants.NS_RESOURCES,
id,
@@ -740,7 +769,7 @@ public final class DescriptorsUtils {
* (e.g. "@+id/something")
*/
public static String getFreeWidgetId(UiElementNode uiNode) {
- String name = uiNode.getDescriptor().getXmlLocalName();
+ String name = getBasename(uiNode.getDescriptor().getXmlLocalName());
return getFreeWidgetId(uiNode.getUiRoot(), name);
}
@@ -891,33 +920,4 @@ public final class DescriptorsUtils {
return false;
}
-
- /**
- * Converts the given attribute value to an XML-attribute-safe value, meaning that
- * single and double quotes are replaced with their corresponding XML entities.
- *
- * @param attrValue the value to be escaped
- * @return the escaped value
- */
- public static String toXmlAttributeValue(String attrValue) {
- // Must escape ' and "
- if (attrValue.indexOf('"') == -1 && attrValue.indexOf('\'') == -1) {
- return attrValue;
- }
-
- int n = attrValue.length();
- StringBuilder sb = new StringBuilder(2 * n);
- for (int i = 0; i < n; i++) {
- char c = attrValue.charAt(i);
- if (c == '"') {
- sb.append("&quot;"); //$NON-NLS-1$
- } else if (c == '\'') {
- sb.append("&apos;"); //$NON-NLS-1$
- } else {
- sb.append(c);
- }
- }
-
- return sb.toString();
- }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java
index 316f020..8ab25cb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java
@@ -16,6 +16,8 @@
package com.android.ide.eclipse.adt.internal.editors.descriptors;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
@@ -39,8 +41,10 @@ import java.util.Set;
* and it will cease to exist when the XML node ceases to exist.
*/
public class ElementDescriptor implements Comparable<ElementDescriptor> {
+ private static final String ELEMENT_ICON_FILENAME = "element"; //$NON-NLS-1$
+
/** The XML element node name. Case sensitive. */
- private final String mXmlName;
+ protected final String mXmlName;
/** The XML element name for the user interface, typically capitalized. */
private final String mUiName;
/** The list of allowed attributes. */
@@ -208,7 +212,7 @@ public class ElementDescriptor implements Comparable<ElementDescriptor> {
*/
public final String getNamespace() {
// For now we hard-code the prefix as being "android"
- if (mXmlName.startsWith("android:")) { //$NON-NLs-1$
+ if (mXmlName.startsWith(ANDROID_NS_NAME_PREFIX)) {
return SdkConstants.NS_RESOURCES;
}
@@ -222,27 +226,52 @@ public class ElementDescriptor implements Comparable<ElementDescriptor> {
}
/**
- * Returns an optional icon for the element.
+ * Returns an icon for the element.
+ * This icon is generic, that is all element descriptors have the same icon
+ * no matter what they represent.
+ *
+ * @return An icon for this element or null.
+ * @see #getCustomizedIcon()
+ */
+ public Image getGenericIcon() {
+ return IconFactory.getInstance().getIcon(ELEMENT_ICON_FILENAME);
+ }
+
+ /**
+ * Returns an optional icon for the element, typically to be used in XML form trees.
+ * <p/>
+ * This icon is customized to the given descriptor, that is different elements
+ * will have different icons.
* <p/>
* By default this tries to return an icon based on the XML name of the element.
* If this fails, it tries to return the default Android logo as defined in the
* plugin. If all fails, it returns null.
*
- * @return An icon for this element or null.
+ * @return An icon for this element. This is never null.
*/
- public Image getIcon() {
+ public Image getCustomizedIcon() {
IconFactory factory = IconFactory.getInstance();
- int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
- int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
+ int color = hasChildren() ? IconFactory.COLOR_BLUE
+ : IconFactory.COLOR_GREEN;
+ int shape = hasChildren() ? IconFactory.SHAPE_RECT
+ : IconFactory.SHAPE_CIRCLE;
String name = mXmlName;
- if (name.indexOf('.') != -1) {
+
+ int pos = name.lastIndexOf('.');
+ if (pos != -1) {
// If the user uses a fully qualified name, such as
- // "android.gesture.GestureOverlayView" in their XML, we need to look up
- // only by basename
- name = name.substring(name.lastIndexOf('.') + 1);
+ // "android.gesture.GestureOverlayView" in their XML, we need to
+ // look up only by basename
+ name = name.substring(pos + 1);
}
Image icon = factory.getIcon(name, color, shape);
- return icon != null ? icon : AdtPlugin.getAndroidLogo();
+ if (icon == null) {
+ icon = getGenericIcon();
+ }
+ if (icon == null) {
+ icon = AdtPlugin.getAndroidLogo();
+ }
+ return icon;
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableContentAssist.java
new file mode 100644
index 0000000..b0bea51
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableContentAssist.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.drawable;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+
+/**
+ * Content Assist Processor for /res/drawable XML files
+ */
+@VisibleForTesting
+public final class DrawableContentAssist extends AndroidContentAssist {
+ public DrawableContentAssist() {
+ super(AndroidTargetData.DESCRIPTOR_DRAWABLE);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableDescriptors.java
new file mode 100644
index 0000000..1e71795
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableDescriptors.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2011 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.editors.drawable;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
+
+import com.android.ide.common.api.IAttributeInfo.Format;
+import com.android.ide.common.resources.platform.AttributeInfo;
+import com.android.ide.common.resources.platform.DeclareStyleableInfo;
+import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ReferenceAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
+import com.android.resources.ResourceType;
+import com.android.sdklib.SdkConstants;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Descriptors for /res/drawable files
+ */
+public class DrawableDescriptors implements IDescriptorProvider {
+ private static final String SDK_URL_BASE =
+ "http://d.android.com/guide/topics/resources/"; //$NON-NLS-1$
+
+ /** The root element descriptor */
+ private ElementDescriptor mDescriptor;
+ /** The root element descriptors */
+ private ElementDescriptor[] mRootDescriptors;
+ private Map<String, ElementDescriptor> nameToDescriptor;
+
+ /** @return the root descriptor. */
+ public ElementDescriptor getDescriptor() {
+ if (mDescriptor == null) {
+ mDescriptor = new ElementDescriptor("", getRootElementDescriptors()); //$NON-NLS-1$
+ }
+
+ return mDescriptor;
+ }
+
+ public ElementDescriptor[] getRootElementDescriptors() {
+ return mRootDescriptors;
+ }
+
+ /**
+ * Returns a descriptor for the given root tag name
+ *
+ * @param tag the tag name to look up a descriptor for
+ * @return a descriptor with the given tag name
+ */
+ public ElementDescriptor getElementDescriptor(String tag) {
+ if (nameToDescriptor == null) {
+ nameToDescriptor = new HashMap<String, ElementDescriptor>();
+ for (ElementDescriptor descriptor : getRootElementDescriptors()) {
+ nameToDescriptor.put(descriptor.getXmlName(), descriptor);
+ }
+ }
+
+ ElementDescriptor descriptor = nameToDescriptor.get(tag);
+ if (descriptor == null) {
+ descriptor = getDescriptor();
+ }
+ return descriptor;
+ }
+
+ public synchronized void updateDescriptors(Map<String, DeclareStyleableInfo> styleMap) {
+ XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_NAME,
+ SdkConstants.NS_RESOURCES);
+ Format[] referenceFormat = new Format[] { Format.REFERENCE };
+
+ List<ElementDescriptor> descriptors = new ArrayList<ElementDescriptor>();
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "animation-list", "Animation List", "AnimationDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "An animation defined in XML that shows a sequence of images in "
+ + "order (like a film)",
+ SDK_URL_BASE + "animation-resource.html#Frame",
+ xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "animated-rotate", "Animated Rotate", "AnimatedRotateDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ // Need docs
+ null /* tooltip */,
+ null /* sdk_url */,
+ xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "bitmap", "BitMap", "BitmapDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "An XML bitmap is a resource defined in XML that points to a bitmap file. "
+ + "The effect is an alias for a raw bitmap file. The XML can "
+ + "specify additional properties for the bitmap such as "
+ + "dithering and tiling.",
+ SDK_URL_BASE + "drawable-resource.html#Bitmap", //$NON-NLS-1$
+ xmlns, null, true /* mandatory */);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "clip", "Clip", "ClipDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "An XML file that defines a drawable that clips another Drawable based on "
+ + "this Drawable's current level value.",
+ SDK_URL_BASE + "drawable-resource.html#Clip", //$NON-NLS-1$
+ xmlns, null, true /*mandatory*/);
+
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "color", "Color", "ColorDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "XML resource that carries a color value (a hexadecimal color)",
+ SDK_URL_BASE + "more-resources.html#Color",
+ xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "inset", "Inset", "InsetDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "An XML file that defines a drawable that insets another drawable by a "
+ + "specified distance. This is useful when a View needs a background "
+ + "drawble that is smaller than the View's actual bounds.",
+ SDK_URL_BASE + "drawable-resource.html#Inset", //$NON-NLS-1$
+ xmlns, null, true /*mandatory*/);
+
+ // Layer list
+
+ // An <item> in a <selector> or <
+ ElementDescriptor layerItem = AnimatorDescriptors.addElement(null, styleMap,
+ "item", "Item", "LayerDrawableItem", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "Defines a drawable to place in the layer drawable, in a position "
+ + "defined by its attributes. Must be a child of a <selector> "
+ + "element. Accepts child <bitmap> elements.",
+ SDK_URL_BASE + "drawable-resource.html#LayerList", //$NON-NLS-1$
+ null, /* extra attribute */
+ null, /* This is wrong -- we can now embed any above drawable
+ (but without xmlns as extra) */
+ false /*mandatory*/);
+ ElementDescriptor[] layerChildren = layerItem != null
+ ? new ElementDescriptor[] { layerItem } : null;
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "layer-list", "Layer List", "LayerDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "A Drawable that manages an array of other Drawables. These are drawn in "
+ + "array order, so the element with the largest index is be drawn on top.",
+ SDK_URL_BASE + "drawable-resource.html#LayerList", //$NON-NLS-1$
+ xmlns,
+ layerChildren,
+ true /*mandatory*/);
+
+ // Level list children
+ ElementDescriptor levelListItem = AnimatorDescriptors.addElement(null, styleMap,
+ "item", "Item", "LevelListDrawableItem", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "Defines a drawable to use at a certain level.",
+ SDK_URL_BASE + "drawable-resource.html#LevelList", //$NON-NLS-1$
+ null, /* extra attribute */
+ null, /* no further children */
+ // TODO: The inflation code seems to show that all drawables can be nested here!
+ false /*mandatory*/);
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "level-list", "Level List", "LevelListDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "An XML file that defines a drawable that manages a number of alternate "
+ + "Drawables, each assigned a maximum numerical value",
+ SDK_URL_BASE + "drawable-resource.html#LevelList", //$NON-NLS-1$
+ xmlns,
+ levelListItem != null ? new ElementDescriptor[] { levelListItem } : null,
+ true /*mandatory*/);
+
+ // Not yet supported
+ //addElement(descriptors, styleMap, "mipmap", "Mipmap", "MipmapDrawable", null,
+ // null /* tooltip */,
+ // null /* sdk_url */,
+ // xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "nine-patch", "Nine Patch", "NinePatchDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "A PNG file with stretchable regions to allow image resizing "
+ + "based on content (.9.png).",
+ SDK_URL_BASE + "drawable-resource.html#NinePatch", //$NON-NLS-1$
+ xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "rotate", "Rotate", "RotateDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ // Need docs
+ null /* tooltip */,
+ null /* sdk_url */,
+ xmlns, null, true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "scale", "Shape", "ScaleDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "An XML file that defines a drawable that changes the size of another Drawable "
+ + "based on its current level value.",
+ SDK_URL_BASE + "drawable-resource.html#Scale", //$NON-NLS-1$
+ xmlns, null, true /*mandatory*/);
+
+ // Selector children
+ ElementDescriptor selectorItem = AnimatorDescriptors.addElement(null, styleMap,
+ "item", "Item", "DrawableStates", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "Defines a drawable to use during certain states, as described by "
+ + "its attributes. Must be a child of a <selector> element.",
+ SDK_URL_BASE + "drawable-resource.html#StateList", //$NON-NLS-1$
+ new ReferenceAttributeDescriptor(
+ ResourceType.DRAWABLE, "drawable", "drawable", //$NON-NLS-1$ //$NON-NLS-2$
+ SdkConstants.NS_RESOURCES,
+ "Reference to a drawable resource.",
+ new AttributeInfo("drawable", referenceFormat)),
+ null, /* This is wrong -- we can now embed any above drawable
+ (but without xmlns as extra) */
+ false /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "selector", "Selector", "StateListDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "An XML file that references different bitmap graphics for different states "
+ + "(for example, to use a different image when a button is pressed).",
+ SDK_URL_BASE + "drawable-resource.html#StateList", //$NON-NLS-1$
+ xmlns,
+ selectorItem != null ? new ElementDescriptor[] { selectorItem } : null,
+ true /*mandatory*/);
+
+ // Shape
+ // Shape children
+ List<ElementDescriptor> shapeChildren = new ArrayList<ElementDescriptor>();
+ // Selector children
+ AnimatorDescriptors.addElement(shapeChildren, styleMap,
+ "size", "Size", "GradientDrawableSize", null, //$NON-NLS-1$ //$NON-NLS-3$
+ null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
+ null /* children */, false /* mandatory */);
+ AnimatorDescriptors.addElement(shapeChildren, styleMap,
+ "gradient", "Gradient", "GradientDrawableGradient", null, //$NON-NLS-1$ //$NON-NLS-3$
+ null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
+ null /* children */, false /* mandatory */);
+ AnimatorDescriptors.addElement(shapeChildren, styleMap,
+ "solid", "Solid", "GradientDrawableSolid", null, //$NON-NLS-1$ //$NON-NLS-3$
+ null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
+ null /* children */, false /* mandatory */);
+ AnimatorDescriptors.addElement(shapeChildren, styleMap,
+ "stroke", "Stroke", "GradientDrawableStroke", null, //$NON-NLS-1$ //$NON-NLS-3$
+ null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
+ null /* children */, false /* mandatory */);
+ AnimatorDescriptors.addElement(shapeChildren, styleMap,
+ "corners", "Corners", "DrawableCorners", null, //$NON-NLS-1$ //$NON-NLS-3$
+ null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
+ null /* children */, false /* mandatory */);
+ AnimatorDescriptors.addElement(shapeChildren, styleMap,
+ "padding", "Padding", "GradientDrawablePadding", null, //$NON-NLS-1$ //$NON-NLS-3$
+ null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
+ null /* children */, false /* mandatory */);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "shape", "Shape", //$NON-NLS-1$
+
+ // The documentation says that a <shape> element creates a ShapeDrawable,
+ // but ShapeDrawable isn't finished and the code currently creates
+ // a GradientDrawable.
+ //"ShapeDrawable", //$NON-NLS-1$
+ "GradientDrawable", //$NON-NLS-1$
+
+ null,
+ "An XML file that defines a geometric shape, including colors and gradients.",
+ SDK_URL_BASE + "drawable-resource.html#Shape", //$NON-NLS-1$
+ xmlns,
+
+ // These are the GradientDrawable children, not the ShapeDrawable children
+ shapeChildren.toArray(new ElementDescriptor[shapeChildren.size()]),
+ true /*mandatory*/);
+
+ AnimatorDescriptors.addElement(descriptors, styleMap,
+ "transition", "Transition", "TransitionDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
+ "An XML file that defines a drawable that can cross-fade between two "
+ + "drawable resources. Each drawable is represented by an <item> "
+ + "element inside a single <transition> element. No more than two "
+ + "items are supported. To transition forward, call startTransition(). "
+ + "To transition backward, call reverseTransition().",
+ SDK_URL_BASE + "drawable-resource.html#Transition", //$NON-NLS-1$
+ xmlns,
+ layerChildren, // children: a TransitionDrawable is a LayerDrawable
+ true /*mandatory*/);
+
+ mRootDescriptors = descriptors.toArray(new ElementDescriptor[descriptors.size()]);
+
+ // A <selector><item> can contain any of the top level drawables
+ if (selectorItem != null) {
+ selectorItem.setChildren(mRootDescriptors);
+ }
+ // Docs says it can accept <bitmap> but code comment suggests any is possible;
+ // test and either use this or just { bitmap }
+ if (layerItem != null) {
+ layerItem.setChildren(mRootDescriptors);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableEditor.java
new file mode 100644
index 0000000..aaaeb4b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableEditor.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.drawable;
+
+import static com.android.ide.eclipse.adt.AdtConstants.EDITORS_NAMESPACE;
+
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Editor for /res/drawable XML files.
+ */
+@SuppressWarnings("restriction")
+public class DrawableEditor extends AndroidXmlEditor {
+ public static final String ID = EDITORS_NAMESPACE + ".drawable.DrawableEditor"; //$NON-NLS-1$
+
+ /** Root node of the UI element hierarchy */
+ private UiElementNode mUiRootNode;
+ /** The tag used at the root */
+ private String mRootTag;
+
+ /**
+ * Creates the form editor for resources XML files.
+ */
+ public DrawableEditor() {
+ super();
+ }
+
+ @Override
+ public UiElementNode getUiRootNode() {
+ return mUiRootNode;
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ return true;
+ }
+
+ @Override
+ protected void createFormPages() {
+ /* Disabled for now; doesn't work quite right
+ try {
+ addPage(new DrawableTreePage(this));
+ } catch (PartInitException e) {
+ AdtPlugin.log(IStatus.ERROR, "Error creating nested page"); //$NON-NLS-1$
+ AdtPlugin.getDefault().getLog().log(e.getStatus());
+ }
+ */
+ }
+
+ @Override
+ protected void setInput(IEditorInput input) {
+ super.setInput(input);
+ if (input instanceof FileEditorInput) {
+ FileEditorInput fileInput = (FileEditorInput) input;
+ IFile file = fileInput.getFile();
+ setPartName(String.format("%1$s",
+ file.getName()));
+ }
+ }
+
+ @Override
+ protected void xmlModelChanged(Document xmlDoc) {
+ Element rootElement = xmlDoc.getDocumentElement();
+ if (rootElement != null) {
+ mRootTag = rootElement.getTagName();
+ }
+
+ initUiRootNode(false /*force*/);
+
+ if (mRootTag != null
+ && !mRootTag.equals(mUiRootNode.getDescriptor().getXmlLocalName())) {
+ AndroidTargetData data = getTargetData();
+ if (data != null) {
+ ElementDescriptor descriptor =
+ data.getDrawableDescriptors().getElementDescriptor(mRootTag);
+ // Replace top level node now that we know the actual type
+
+ // Disconnect from old
+ mUiRootNode.setEditor(null);
+ mUiRootNode.setXmlDocument(null);
+
+ // Create new
+ mUiRootNode = descriptor.createUiNode();
+ mUiRootNode.setXmlDocument(xmlDoc);
+ mUiRootNode.setEditor(this);
+ }
+ }
+
+ if (mUiRootNode.getDescriptor() instanceof DocumentDescriptor) {
+ mUiRootNode.loadFromXmlNode(xmlDoc);
+ } else {
+ mUiRootNode.loadFromXmlNode(rootElement);
+ }
+
+ super.xmlModelChanged(xmlDoc);
+ }
+
+ @Override
+ protected void initUiRootNode(boolean force) {
+ // The manifest UI node is always created, even if there's no corresponding XML node.
+ if (mUiRootNode == null || force) {
+ ElementDescriptor descriptor;
+ boolean reload = false;
+ AndroidTargetData data = getTargetData();
+ if (data == null) {
+ descriptor = new DocumentDescriptor("temp", null /*children*/);
+ } else {
+ descriptor = data.getDrawableDescriptors().getElementDescriptor(mRootTag);
+ reload = true;
+ }
+ mUiRootNode = descriptor.createUiNode();
+ mUiRootNode.setEditor(this);
+
+ if (reload) {
+ onDescriptorsChanged();
+ }
+ }
+ }
+
+ private void onDescriptorsChanged() {
+ IStructuredModel model = getModelForRead();
+ if (model != null) {
+ try {
+ Node node = getXmlDocument(model).getDocumentElement();
+ mUiRootNode.reloadFromXmlNode(node);
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableSourceViewerConfig.java
new file mode 100644
index 0000000..eef6a9e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/drawable/DrawableSourceViewerConfig.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.drawable;
+
+
+import com.android.ide.eclipse.adt.internal.editors.AndroidSourceViewerConfig;
+
+/**
+ * Source Viewer Configuration for drawable files
+ */
+public class DrawableSourceViewerConfig extends AndroidSourceViewerConfig {
+
+ public DrawableSourceViewerConfig() {
+ super(new DrawableContentAssist());
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/AbstractPropertiesFieldsPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/AbstractPropertiesFieldsPart.java
index 06169d2..0d72614 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/AbstractPropertiesFieldsPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/AbstractPropertiesFieldsPart.java
@@ -34,6 +34,7 @@ import org.eclipse.ui.forms.widgets.Section;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
/**
* Section part for editing fields of a properties file in an Export editor.
@@ -302,13 +303,15 @@ abstract class AbstractPropertiesFieldsPart extends ManifestSectionPart {
}
// Clear the text of any keyword we didn't find in the document
- for (String key : allKeywords) {
+ Iterator<String> iterator = allKeywords.iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
Control field = mNameToField.get(key);
if (field != null) {
try {
mInternalTextUpdate = true;
setFieldText(field, "");
- allKeywords.remove(key);
+ iterator.remove();
} finally {
mInternalTextUpdate = false;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportEditor.java
index 50d9fd8..769f74e 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportEditor.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.editors.export;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.editors.AndroidTextEditor;
import org.eclipse.core.resources.IFile;
@@ -32,7 +32,7 @@ import org.eclipse.ui.part.FileEditorInput;
*/
public class ExportEditor extends AndroidTextEditor {
- public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".text.ExportEditor"; //$NON-NLS-1$
+ public static final String ID = AdtConstants.EDITORS_NAMESPACE + ".text.ExportEditor"; //$NON-NLS-1$
private ExportPropertiesPage mExportPropsPage;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ContextPullParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ContextPullParser.java
index d8911e4..7c40097 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ContextPullParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ContextPullParser.java
@@ -22,6 +22,8 @@ import static com.android.ide.common.layout.LayoutConstants.VALUE_FILL_PARENT;
import static com.android.ide.common.layout.LayoutConstants.VALUE_MATCH_PARENT;
import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.layoutlib.api.IXmlPullParser;
import com.android.sdklib.SdkConstants;
import org.kxml2.io.KXmlParser;
@@ -35,23 +37,22 @@ import org.kxml2.io.KXmlParser;
*/
public class ContextPullParser extends KXmlParser implements ILayoutPullParser {
- private final String mName;
- private final ILayoutPullParser mEmbeddedParser;
+ private final IProjectCallback mProjectCallback;
- public ContextPullParser(String name, ILayoutPullParser embeddedParser) {
+ public ContextPullParser(IProjectCallback projectCallback) {
super();
- mName = name;
- mEmbeddedParser = embeddedParser;
+ mProjectCallback = projectCallback;
}
// --- Layout lib API methods
+ /**
+ * this is deprecated but must still be implemented for older layout libraries.
+ * @deprecated use {@link IProjectCallback#getParser(String)}.
+ */
+ @Deprecated
public ILayoutPullParser getParser(String layoutName) {
- if (mName.equals(layoutName)) {
- return mEmbeddedParser;
- }
-
- return null;
+ return mProjectCallback.getParser(layoutName);
}
public Object getViewCookie() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java
index 4b313f6..d55db31 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java
@@ -16,16 +16,18 @@
package com.android.ide.eclipse.adt.internal.editors.layout;
+import com.android.annotations.VisibleForTesting;
import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
/**
* Content Assist Processor for /res/layout XML files
*/
-class LayoutContentAssist extends AndroidContentAssist {
+@VisibleForTesting
+public final class LayoutContentAssist extends AndroidContentAssist {
/**
- * Constructor for LayoutContentAssist
+ * Constructor for LayoutContentAssist
*/
public LayoutContentAssist() {
super(AndroidTargetData.DESCRIPTOR_LAYOUT);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
index 70e062a..bccc7d0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.editors.layout;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
@@ -25,6 +25,7 @@ import com.android.ide.eclipse.adt.internal.editors.descriptors.IUnknownDescript
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.CustomViewDescriptorService;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.OutlinePage;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.PropertySheetPage;
@@ -40,6 +41,7 @@ import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
@@ -64,11 +66,14 @@ import java.util.Set;
*/
public class LayoutEditor extends AndroidXmlEditor implements IShowEditorInput, IPartListener {
- public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".layout.LayoutEditor"; //$NON-NLS-1$
+ public static final String ID = AdtConstants.EDITORS_NAMESPACE + ".layout.LayoutEditor"; //$NON-NLS-1$
/** Root node of the UI element hierarchy */
private UiDocumentNode mUiRootNode;
+ /** Flag used to ignore XML model updates */
+ private boolean mIgnoreXmlUpdate;
+
private GraphicalEditorPart mGraphicalEditor;
private int mGraphicalEditorIndex;
/** Implementation of the {@link IContentOutlinePage} for this editor */
@@ -285,12 +290,45 @@ public class LayoutEditor extends AndroidXmlEditor implements IShowEditorInput,
}
/**
+ * Controls whether XML models are ignored or not. Can be used in conjunction with the
+ * {@link #refreshXmlModel()} method to suppress many smaller individual edits and
+ * then flush a large model update at the end. This is for example used for
+ * refactoring, such that we don't update the model as each individual
+ * {@link TextEdit} is applied, and instead we process them all in a single refresh at
+ * the end.
+ *
+ * @param ignore when true, ignore all subsequent XML model updates, when false start
+ * processing XML model updates again
+ */
+ public void setIgnoreXmlUpdate(boolean ignore) {
+ mIgnoreXmlUpdate = ignore;
+ }
+
+ /** Performs a complete refresh of the XML model */
+ public void refreshXmlModel() {
+ Document xmlDoc = mUiRootNode.getXmlDocument();
+
+ initUiRootNode(true /*force*/);
+ mUiRootNode.loadFromXmlNode(xmlDoc);
+ // update the model first, since it is used by the viewers.
+ super.xmlModelChanged(xmlDoc);
+
+ if (mGraphicalEditor != null) {
+ mGraphicalEditor.onXmlModelChanged();
+ }
+ }
+
+ /**
* Processes the new XML Model, which XML root node is given.
*
* @param xml_doc The XML document, if available, or null if none exists.
*/
@Override
protected void xmlModelChanged(Document xml_doc) {
+ if (mIgnoreXmlUpdate) {
+ return;
+ }
+
// init the ui root on demand
initUiRootNode(false /*force*/);
@@ -351,7 +389,7 @@ public class LayoutEditor extends AndroidXmlEditor implements IShowEditorInput,
ISourceViewer textViewer = getStructuredSourceViewer();
int caretOffset = textViewer.getTextWidget().getCaretOffset();
if (caretOffset >= 0) {
- Node node = AndroidXmlEditor.getNode(textViewer.getDocument(), caretOffset);
+ Node node = DomUtilities.getNode(textViewer.getDocument(), caretOffset);
if (node != null && mGraphicalEditor != null) {
mGraphicalEditor.select(node);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java
index 380381b..ba77ce9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java
@@ -16,11 +16,11 @@
package com.android.ide.eclipse.adt.internal.editors.layout;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceFolder;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IResourceEventListener;
@@ -37,6 +37,7 @@ import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -62,7 +63,6 @@ public final class LayoutReloadMonitor {
public boolean code = false;
/** any non-layout resource changes */
public boolean resources = false;
- public boolean layout = false;
public boolean rClass = false;
public boolean localeList = false;
public boolean manifest = false;
@@ -174,7 +174,7 @@ public final class LayoutReloadMonitor {
boolean hasAndroidNature = false;
try {
- hasAndroidNature = project.hasNature(AndroidConstants.NATURE_DEFAULT);
+ hasAndroidNature = project.hasNature(AdtConstants.NATURE_DEFAULT);
} catch (CoreException e) {
// do nothing if the nature cannot be queried.
return;
@@ -192,7 +192,7 @@ public final class LayoutReloadMonitor {
for (IProject p : referencingProjects) {
try {
- hasAndroidNature = p.hasNature(AndroidConstants.NATURE_DEFAULT);
+ hasAndroidNature = p.hasNature(AdtConstants.NATURE_DEFAULT);
} catch (CoreException e) {
// do nothing if the nature cannot be queried.
continue;
@@ -221,7 +221,7 @@ public final class LayoutReloadMonitor {
// here we only care about code change (so change for .class files).
// Resource changes is handled by the IResourceListener.
- if (AndroidConstants.EXT_CLASS.equals(file.getFileExtension())) {
+ if (AdtConstants.EXT_CLASS.equals(file.getFileExtension())) {
if (file.getName().matches("R[\\$\\.](.*)")) {
// this is a R change!
if (changeFlags == null) {
@@ -353,21 +353,17 @@ public final class LayoutReloadMonitor {
// now check that the file is *NOT* a layout file (those automatically trigger a layout
// reload and we don't want to do it twice.)
- ResourceType[] resTypes = file.getResourceTypes();
+ Collection<ResourceType> resTypes = file.getResourceTypes();
// it's unclear why but there has been cases of resTypes being empty!
- if (resTypes.length > 0) {
+ if (resTypes.size() > 0) {
// this is a resource change, that may require a layout redraw!
if (changeFlags == null) {
changeFlags = new ChangeFlags();
mProjectFlags.put(project, changeFlags);
}
- if (resTypes[0] != ResourceType.LAYOUT) {
- changeFlags.resources = true;
- } else {
- changeFlags.layout = true;
- }
+ changeFlags.resources = true;
}
}
};
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/MatchingStrategy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/MatchingStrategy.java
index c6c5261..4ef249d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/MatchingStrategy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/MatchingStrategy.java
@@ -16,9 +16,9 @@
package com.android.ide.eclipse.adt.internal.editors.layout;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
+import com.android.ide.common.resources.ResourceFolder;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.resources.ResourceFolderType;
import org.eclipse.core.resources.IFile;
import org.eclipse.ui.IEditorInput;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java
index 976dfaf..7383659 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java
@@ -16,11 +16,24 @@
package com.android.ide.eclipse.adt.internal.editors.layout;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_PKG_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.CALENDAR_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.EXPANDABLE_LIST_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.LIST_VIEW;
+
+import com.android.ide.common.rendering.LayoutLibrary;
+import com.android.ide.common.rendering.api.AdapterBinding;
+import com.android.ide.common.rendering.api.DataBindingItem;
+import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.legacy.LegacyCallback;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectClassLoader;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
@@ -43,7 +56,6 @@ import java.util.TreeSet;
* {@link ILegacyCallback}
*/
public final class ProjectCallback extends LegacyCallback {
-
private final HashMap<String, Class<?>> mLoadedClasses = new HashMap<String, Class<?>>();
private final Set<String> mMissingClasses = new TreeSet<String>();
private final Set<String> mBrokenClasses = new TreeSet<String>();
@@ -54,16 +66,23 @@ public final class ProjectCallback extends LegacyCallback {
private String mNamespace;
private ProjectClassLoader mLoader = null;
private LayoutLog mLogger;
+ private LayoutLibrary mLayoutLib;
+
+ private String mLayoutName;
+ private ILayoutPullParser mLayoutEmbeddedParser;
+
/**
* Creates a new {@link ProjectCallback} to be used with the layout lib.
*
- * @param classLoader The class loader that was used to load layoutlib.jar
+ * @param layoutLib The layout library this callback is going to be invoked from
* @param projectRes the {@link ProjectResources} for the project.
* @param project the project.
*/
- public ProjectCallback(ClassLoader classLoader, ProjectResources projectRes, IProject project) {
- mParentClassLoader = classLoader;
+ public ProjectCallback(LayoutLibrary layoutLib,
+ ProjectResources projectRes, IProject project) {
+ mLayoutLib = layoutLib;
+ mParentClassLoader = layoutLib.getClassLoader();
mProjectRes = projectRes;
mProject = project;
}
@@ -199,7 +218,7 @@ public final class ProjectCallback extends LegacyCallback {
ManifestData manifestData = AndroidManifestHelper.parseForData(mProject);
if (manifestData != null) {
String javaPackage = manifestData.getPackage();
- mNamespace = String.format(AndroidConstants.NS_CUSTOM_RESOURCES, javaPackage);
+ mNamespace = String.format(AdtConstants.NS_CUSTOM_RESOURCES, javaPackage);
}
}
@@ -208,7 +227,7 @@ public final class ProjectCallback extends LegacyCallback {
public Pair<ResourceType, String> resolveResourceId(int id) {
if (mProjectRes != null) {
- return mProjectRes.resolveResourceValue(id);
+ return mProjectRes.resolveResourceId(id);
}
return null;
@@ -216,7 +235,7 @@ public final class ProjectCallback extends LegacyCallback {
public String resolveResourceId(int[] id) {
if (mProjectRes != null) {
- return mProjectRes.resolveResourceValue(id);
+ return mProjectRes.resolveStyleable(id);
}
return null;
@@ -224,7 +243,7 @@ public final class ProjectCallback extends LegacyCallback {
public Integer getResourceId(ResourceType type, String name) {
if (mProjectRes != null) {
- return mProjectRes.getResourceValue(type, name);
+ return mProjectRes.getResourceId(type, name);
}
return null;
@@ -336,4 +355,151 @@ public final class ProjectCallback extends LegacyCallback {
constructor.setAccessible(true);
return constructor.newInstance(constructorParameters);
}
+
+ public void setLayoutParser(String layoutName, ILayoutPullParser layoutParser) {
+ mLayoutName = layoutName;
+ mLayoutEmbeddedParser = layoutParser;
+ }
+
+ public ILayoutPullParser getParser(String layoutName) {
+ if (layoutName.equals(mLayoutName)) {
+ return mLayoutEmbeddedParser;
+ }
+
+ return null;
+ }
+
+ public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
+ ResourceReference itemRef,
+ int fullPosition, int typePosition, int fullChildPosition, int typeChildPosition,
+ ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue) {
+
+ // Special case for the palette preview
+ if (viewAttribute == ViewAttribute.TEXT
+ && adapterView.getName().startsWith("android_widget_")) { //$NON-NLS-1$
+ String name = adapterView.getName();
+ if (viewRef.getName().equals("text2")) { //$NON-NLS-1$
+ return "Sub Item";
+ }
+ if (fullPosition == 0) {
+ String viewName = name.substring("android_widget_".length());
+ if (viewName.equals(EXPANDABLE_LIST_VIEW)) {
+ return "ExpandableList"; // ExpandableListView is too wide, character-wraps
+ }
+ return viewName;
+ } else {
+ return "Next Item";
+ }
+ }
+
+ if (itemRef.isFramework()) {
+ // Special case for list_view_item_2 and friends
+ if (viewRef.getName().equals("text2")) { //$NON-NLS-1$
+ return "Sub Item " + (fullPosition + 1);
+ }
+ }
+
+ if (viewAttribute == ViewAttribute.TEXT && ((String) defaultValue).length() == 0) {
+ return "Item " + (fullPosition + 1);
+ }
+
+ return null;
+ }
+
+ /**
+ * For the given class, finds and returns the nearest super class which is a ListView
+ * or an ExpandableListView, or returns null.
+ *
+ * @param clz the class of the view object
+ * @return the fully qualified class name of the list view ancestor, or null if there
+ * is no list view ancestor
+ */
+ public static String getListViewFqcn(Class<?> clz) {
+ String fqcn = clz.getName();
+ if (fqcn.endsWith(LIST_VIEW)) { // including EXPANDABLE_LIST_VIEW
+ return fqcn;
+ } else if (fqcn.startsWith(ANDROID_PKG_PREFIX)) {
+ return null;
+ }
+ Class<?> superClass = clz.getSuperclass();
+ if (superClass != null) {
+ return getListViewFqcn(superClass);
+ } else {
+ // Should not happen; we would have encountered android.view.View first,
+ // and it should have been covered by the ANDROID_PKG_PREFIX case above.
+ return null;
+ }
+ }
+
+ /**
+ * Looks at the parent-chain of the view and if it finds a custom view, or a
+ * CalendarView, within the given distance then it returns true. A ListView within a
+ * CalendarView should not be assigned a custom list view type because it sets its own
+ * and then attempts to cast the layout to its own type which would fail if the normal
+ * default list item binding is used.
+ */
+ private boolean isWithinIllegalParent(Object viewObject, int depth) {
+ String fqcn = viewObject.getClass().getName();
+ if (fqcn.endsWith(CALENDAR_VIEW) || !fqcn.startsWith(ANDROID_PKG_PREFIX)) {
+ return true;
+ }
+
+ if (depth > 0) {
+ Result result = mLayoutLib.getViewParent(viewObject);
+ if (result.isSuccess()) {
+ Object parent = result.getData();
+ if (parent != null) {
+ return isWithinIllegalParent(parent, depth -1);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public AdapterBinding getAdapterBinding(ResourceReference adapterView, Object adapterCookie,
+ Object viewObject) {
+ // Look for user-recorded preference for layout to be used for previews
+ if (adapterCookie instanceof UiViewElementNode) {
+ UiViewElementNode uiNode = (UiViewElementNode) adapterCookie;
+ LayoutMetadata metadata = LayoutMetadata.get();
+ AdapterBinding binding = metadata.getNodeBinding(viewObject, uiNode);
+ if (binding != null) {
+ return binding;
+ }
+ }
+
+ if (viewObject == null) {
+ return null;
+ }
+
+ // Is this a ListView or ExpandableListView? If so, return its fully qualified
+ // class name, otherwise return null. This is used to filter out other types
+ // of AdapterViews (such as Spinners) where we don't want to use the list item
+ // binding.
+ String listFqcn = getListViewFqcn(viewObject.getClass());
+ if (listFqcn == null) {
+ return null;
+ }
+
+ // Is this ListView nested within an "illegal" container, such as a CalendarView?
+ // If so, don't change the bindings below. Some views, such as CalendarView, and
+ // potentially some custom views, might be doing specific things with the ListView
+ // that could break if we add our own list binding, so for these leave the list
+ // alone.
+ if (isWithinIllegalParent(viewObject, 2)) {
+ return null;
+ }
+
+ AdapterBinding binding = new AdapterBinding(12);
+ if (listFqcn.endsWith(EXPANDABLE_LIST_VIEW)) {
+ binding.addItem(new DataBindingItem(LayoutMetadata.DEFAULT_EXPANDABLE_LIST_ITEM,
+ true /* isFramework */, 1));
+ } else {
+ binding.addItem(new DataBindingItem(LayoutMetadata.DEFAULT_LIST_ITEM,
+ true /* isFramework */, 1));
+ }
+
+ return binding;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/WidgetPullParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/WidgetPullParser.java
index 153a2d2..07e6a91 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/WidgetPullParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/WidgetPullParser.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.editors.layout;
import com.android.ide.common.rendering.api.ILayoutPullParser;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.sdklib.SdkConstants;
@@ -43,7 +43,7 @@ public class WidgetPullParser extends BasePullParser {
public WidgetPullParser(ViewElementDescriptor descriptor) {
mDescriptor = descriptor;
- String[] segments = mDescriptor.getFullClassName().split(AndroidConstants.RE_DOT);
+ String[] segments = mDescriptor.getFullClassName().split(AdtConstants.RE_DOT);
mAttributes[0][1] = segments[segments.length-1];
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigEditDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigEditDialog.java
index 1dc5c27..d9b3911 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigEditDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigEditDialog.java
@@ -16,14 +16,14 @@
package com.android.ide.eclipse.adt.internal.editors.layout.configuration;
+import com.android.ide.common.resources.configuration.DockModeQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.LanguageQualifier;
+import com.android.ide.common.resources.configuration.NightModeQualifier;
+import com.android.ide.common.resources.configuration.RegionQualifier;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.ide.common.resources.configuration.VersionQualifier;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-import com.android.ide.eclipse.adt.internal.resources.configurations.DockModeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NightModeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.VersionQualifier;
import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java
index 20aacba..9812c2a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java
@@ -17,8 +17,8 @@
package com.android.ide.eclipse.adt.internal.editors.layout.configuration;
import com.android.ddmuilib.TableHelper;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice;
import com.android.ide.eclipse.adt.internal.sdk.LayoutDeviceManager;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
index 0e927fa..d304303 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
@@ -16,24 +16,30 @@
package com.android.ide.eclipse.adt.internal.editors.layout.configuration;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_STYLE;
+
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceFolder;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.common.resources.configuration.DockModeQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.LanguageQualifier;
+import com.android.ide.common.resources.configuration.NightModeQualifier;
+import com.android.ide.common.resources.configuration.PixelDensityQualifier;
+import com.android.ide.common.resources.configuration.RegionQualifier;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.VersionQualifier;
import com.android.ide.common.sdk.LoadStatus;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.resources.configurations.DockModeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NightModeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
-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.VersionQualifier;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
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.LayoutDevice;
@@ -43,14 +49,19 @@ import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice.DeviceConfig;
import com.android.resources.Density;
import com.android.resources.DockMode;
import com.android.resources.NightMode;
+import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenSize;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.util.SparseIntArray;
+import com.android.util.Pair;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.draw2d.geometry.Rectangle;
@@ -66,9 +77,12 @@ import org.eclipse.swt.widgets.Label;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.SortedSet;
/**
@@ -97,9 +111,23 @@ import java.util.SortedSet;
* loading.<br>
*/
public class ConfigurationComposite extends Composite {
+ private final static String SEP = ":"; //$NON-NLS-1$
+ private final static String SEP_LOCALE = "-"; //$NON-NLS-1$
+ /**
+ * Setting name for project-wide setting controlling rendering target and locale which
+ * is shared for all files
+ */
+ public final static QualifiedName NAME_RENDER_STATE =
+ new QualifiedName(AdtPlugin.PLUGIN_ID, "render");//$NON-NLS-1$
+
+ /**
+ * Settings name for file-specific configuration preferences, such as which theme or
+ * device to render the current layout with
+ */
public final static QualifiedName NAME_CONFIG_STATE =
new QualifiedName(AdtPlugin.PLUGIN_ID, "state");//$NON-NLS-1$
+
private final static String THEME_SEPARATOR = "----------"; //$NON-NLS-1$
private final static int LOCALE_LANG = 0;
@@ -116,7 +144,13 @@ public class ConfigurationComposite extends Composite {
private Combo mThemeCombo;
private Combo mTargetCombo;
- private int mPlatformThemeCount = 0;
+ /**
+ * List of booleans, matching item for item the theme names in the mThemeCombo
+ * combobox, where each boolean represents whether the corresponding theme is a
+ * project theme
+ */
+ private List<Boolean> mIsProjectTheme = new ArrayList<Boolean>(40);
+
/** updates are disabled if > 0 */
private int mDisableUpdates = 0;
@@ -164,6 +198,12 @@ public class ConfigurationComposite extends Composite {
void onConfigurationChange();
/**
+ * Called after a device has changed (in addition to {@link #onConfigurationChange}
+ * getting called)
+ */
+ void onDevicePostChange();
+
+ /**
* Called when the current theme changes. The theme can be queried with
* {@link ConfigurationComposite#getTheme()}.
*/
@@ -187,11 +227,12 @@ public class ConfigurationComposite extends Composite {
*/
void onRenderingTargetPostChange(IAndroidTarget target);
- ProjectResources getProjectResources();
- ProjectResources getFrameworkResources();
- ProjectResources getFrameworkResources(IAndroidTarget target);
+ ResourceRepository getProjectResources();
+ ResourceRepository getFrameworkResources();
+ ResourceRepository getFrameworkResources(IAndroidTarget target);
Map<ResourceType, Map<String, ResourceValue>> getConfiguredProjectResources();
Map<ResourceType, Map<String, ResourceValue>> getConfiguredFrameworkResources();
+ String getIncludedWithin();
}
/**
@@ -199,9 +240,6 @@ public class ConfigurationComposite extends Composite {
* rendering to its original configuration.
*/
private class ConfigState {
- private final static String SEP = ":"; //$NON-NLS-1$
- private final static String SEP_LOCALE = "-"; //$NON-NLS-1$
-
LayoutDevice device;
String configName;
ResourceQualifier[] locale;
@@ -220,7 +258,7 @@ public class ConfigurationComposite extends Composite {
sb.append(SEP);
sb.append(configName);
sb.append(SEP);
- if (locale != null) {
+ if (isLocaleSpecificLayout() && locale != null) {
if (locale[0] != null && locale[1] != null) {
// locale[0]/[1] can be null sometimes when starting Eclipse
sb.append(((LanguageQualifier) locale[0]).getValue());
@@ -235,10 +273,10 @@ public class ConfigurationComposite extends Composite {
sb.append(SEP);
sb.append(night.getResourceValue());
sb.append(SEP);
- if (target != null) {
- sb.append(targetToString(target));
- sb.append(SEP);
- }
+
+ // We used to store the render target here in R9. Leave a marker
+ // to ensure that we don't reuse this slot; add new extra fields after it.
+ sb.append(SEP);
}
return sb.toString();
@@ -254,6 +292,8 @@ public class ConfigurationComposite extends Composite {
if (config != null) {
configName = values[1];
+ // Load locale. Note that this can get overwritten by the
+ // project-wide settings read below.
locale = new ResourceQualifier[2];
String locales[] = values[2].split(SEP_LOCALE);
if (locales.length >= 2) {
@@ -275,12 +315,17 @@ public class ConfigurationComposite extends Composite {
night = NightMode.NOTNIGHT;
}
- if (values.length == 7 && mTargetList != null) {
- target = stringToTarget(values[6]);
- } else {
- // No render target stored; try to find the best default
- target = findDefaultRenderTarget();
+ // element 7/values[6]: used to store render target in R9.
+ // No longer stored here. If adding more data, make
+ // sure you leave 7 alone.
+
+ Pair<ResourceQualifier[], IAndroidTarget> pair = loadRenderState();
+
+ // We only use the "global" setting
+ if (!isLocaleSpecificLayout()) {
+ locale = pair.getFirst();
}
+ target = pair.getSecond();
return true;
}
@@ -293,67 +338,40 @@ public class ConfigurationComposite extends Composite {
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- if (device != null) {
- sb.append(device.getName());
- } else {
- sb.append("null");
- }
- sb.append(SEP);
- sb.append(configName);
- sb.append(SEP);
- if (locale != null) {
- sb.append(((LanguageQualifier) locale[0]).getValue());
- sb.append(SEP_LOCALE);
- sb.append(((RegionQualifier) locale[1]).getValue());
- }
- sb.append(SEP);
- sb.append(theme);
- sb.append(SEP);
- sb.append(dock.getResourceValue());
- sb.append(SEP);
- sb.append(night.getResourceValue());
- sb.append(SEP);
-
- if (target != null) {
- sb.append(targetToString(target));
- sb.append(SEP);
- }
-
- return sb.toString();
+ return getData();
}
+ }
- /**
- * Returns a String id to represent an {@link IAndroidTarget} which can be translated
- * back to an {@link IAndroidTarget} by the matching {@link #stringToTarget}. The id
- * will never contain the {@link #SEP} character.
- *
- * @param target the target to return an id for
- * @return an id for the given target; never null
- */
- private String targetToString(IAndroidTarget target) {
- return target.getFullName().replace(SEP, ""); //$NON-NLS-1$
- }
+ /**
+ * Returns a String id to represent an {@link IAndroidTarget} which can be translated
+ * back to an {@link IAndroidTarget} by the matching {@link #stringToTarget}. The id
+ * will never contain the {@link #SEP} character.
+ *
+ * @param target the target to return an id for
+ * @return an id for the given target; never null
+ */
+ private String targetToString(IAndroidTarget target) {
+ return target.getFullName().replace(SEP, ""); //$NON-NLS-1$
+ }
- /**
- * Returns an {@link IAndroidTarget} that corresponds to the given id that was
- * originally returned by {@link #targetToString}. May be null, if the platform is no
- * longer available, or if the platform list has not yet been initialized.
- *
- * @param id the id that corresponds to the desired platform
- * @return an {@link IAndroidTarget} that matches the given id, or null
- */
- private IAndroidTarget stringToTarget(String id) {
- if (mTargetList != null && mTargetList.size() > 0) {
- for (IAndroidTarget target : mTargetList) {
- if (id.equals(targetToString(target))) {
- return target;
- }
+ /**
+ * Returns an {@link IAndroidTarget} that corresponds to the given id that was
+ * originally returned by {@link #targetToString}. May be null, if the platform is no
+ * longer available, or if the platform list has not yet been initialized.
+ *
+ * @param id the id that corresponds to the desired platform
+ * @return an {@link IAndroidTarget} that matches the given id, or null
+ */
+ private IAndroidTarget stringToTarget(String id) {
+ if (mTargetList != null && mTargetList.size() > 0) {
+ for (IAndroidTarget target : mTargetList) {
+ if (id.equals(targetToString(target))) {
+ return target;
}
}
-
- return null;
}
+
+ return null;
}
/**
@@ -377,11 +395,11 @@ public class ConfigurationComposite extends Composite {
GridLayout gl;
GridData gd;
- int cols = 6; // device+config+separator*2+theme+apiLevel
+ int cols = 7; // device+config+dock+day+separator*2+theme
- // ---- First line: locale, day/night, dock, editing config display.
+ // ---- First line: editing config display, locale, theme, create-button
Composite labelParent = new Composite(this, SWT.NONE);
- labelParent.setLayout(gl = new GridLayout(6, false));
+ labelParent.setLayout(gl = new GridLayout(5, false));
gl.marginWidth = gl.marginHeight = 0;
gl.marginTop = 3;
labelParent.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
@@ -407,25 +425,13 @@ public class ConfigurationComposite extends Composite {
mLocaleCombo.add("Locale"); //$NON-NLS-1$ // Dummy place holders
mLocaleCombo.add("Locale"); //$NON-NLS-1$
- mDockCombo = new Combo(labelParent, SWT.DROP_DOWN | SWT.READ_ONLY);
- for (DockMode mode : DockMode.values()) {
- mDockCombo.add(mode.getLongDisplayValue());
- }
- mDockCombo.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onDockChange();
- }
- });
-
- mNightCombo = new Combo(labelParent, SWT.DROP_DOWN | SWT.READ_ONLY);
- for (NightMode mode : NightMode.values()) {
- mNightCombo.add(mode.getLongDisplayValue());
- }
- mNightCombo.addSelectionListener(new SelectionAdapter() {
+ mTargetCombo = new Combo(labelParent, SWT.DROP_DOWN | SWT.READ_ONLY);
+ mTargetCombo.add("Android AOSP"); //$NON-NLS-1$ // Dummy place holders
+ mTargetCombo.add("Android AOSP"); //$NON-NLS-1$
+ mTargetCombo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- onDayChange();
+ onRenderingTargetChange();
}
});
@@ -474,31 +480,41 @@ public class ConfigurationComposite extends Composite {
GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
gd.heightHint = 0;
- mThemeCombo = new Combo(this, SWT.READ_ONLY | SWT.DROP_DOWN);
- mThemeCombo.setLayoutData(new GridData(
- GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mThemeCombo.setEnabled(false);
-
- mThemeCombo.addSelectionListener(new SelectionAdapter() {
+ mDockCombo = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ mDockCombo.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL
+ | GridData.GRAB_HORIZONTAL));
+ for (DockMode mode : DockMode.values()) {
+ mDockCombo.add(mode.getLongDisplayValue());
+ }
+ mDockCombo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- onThemeChange();
+ onDockChange();
}
});
- // second separator
- separator = new Label(this, SWT.SEPARATOR | SWT.VERTICAL);
- separator.setLayoutData(gd = new GridData(
- GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
- gd.heightHint = 0;
+ mNightCombo = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ mNightCombo.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL
+ | GridData.GRAB_HORIZONTAL));
+ for (NightMode mode : NightMode.values()) {
+ mNightCombo.add(mode.getLongDisplayValue());
+ }
+ mNightCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onDayChange();
+ }
+ });
- mTargetCombo = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
- mTargetCombo.setLayoutData(new GridData(
+ mThemeCombo = new Combo(this, SWT.READ_ONLY | SWT.DROP_DOWN);
+ mThemeCombo.setLayoutData(new GridData(
GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mTargetCombo.addSelectionListener(new SelectionAdapter() {
+ mThemeCombo.setEnabled(false);
+
+ mThemeCombo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- onRenderingTargetChange();
+ onThemeChange();
}
});
}
@@ -679,8 +695,6 @@ public class ConfigurationComposite extends Composite {
loadedConfigData = mState.setData(data);
}
- // update the themes and locales.
- updateThemes();
updateLocales();
// If the current state was loaded from the persistent storage, we update the
@@ -710,6 +724,14 @@ public class ConfigurationComposite extends Composite {
}
}
+ // Update themes. This is done after updating the devices above,
+ // since we want to look at the chosen device size to decide
+ // what the default theme (for example, with Honeycomb we choose
+ // Holo as the default theme but only if the screen size is XLARGE
+ // (and of course only if the manifest does not specify another
+ // default theme).
+ updateThemes();
+
// update the string showing the config value
updateConfigDisplay(mEditedConfig);
@@ -767,6 +789,26 @@ public class ConfigurationComposite extends Composite {
}
}
+ private static class ConfigMatch {
+ final FolderConfiguration testConfig;
+ final LayoutDevice device;
+ final String name;
+ final ConfigBundle bundle;
+
+ public ConfigMatch(FolderConfiguration testConfig,
+ LayoutDevice device, String name, ConfigBundle bundle) {
+ this.testConfig = testConfig;
+ this.device = device;
+ this.name = name;
+ this.bundle = bundle;
+ }
+
+ @Override
+ public String toString() {
+ return device.getName() + " - " + name;
+ }
+ }
+
/**
* Finds a device/config that can display {@link #mEditedConfig}.
* <p/>Once found the device and config combos are set to the config.
@@ -775,23 +817,19 @@ public class ConfigurationComposite extends Composite {
* the current config. This must only be true if the current config is compatible.
*/
private void findAndSetCompatibleConfig(boolean favorCurrentConfig) {
- LayoutDevice anyDeviceMatch = null; // a compatible device/config/locale
- String anyConfigMatchName = null;
- ConfigBundle anyConfigBundle = null;
+ // list of compatible device/config/locale
+ List<ConfigMatch> anyMatches = new ArrayList<ConfigMatch>();
- LayoutDevice bestDeviceMatch = null; // an actual best match
- String bestConfigMatchName = null;
- ConfigBundle bestConfigBundle = null;
-
- FolderConfiguration testConfig = new FolderConfiguration();
+ // list of actual best match (ie the file is a best match for the device/config)
+ List<ConfigMatch> bestMatches = new ArrayList<ConfigMatch>();
// get a locale that match the host locale roughly (may not be exact match on the region.)
int localeHostMatch = getLocaleMatch();
// build a list of combinations of non standard qualifiers to add to each device's
// qualifier set when testing for a match.
- // These qualifiers are: locale, nightmode, car dock.
- List<ConfigBundle> addConfig = new ArrayList<ConfigBundle>();
+ // These qualifiers are: locale, night-mode, car dock.
+ List<ConfigBundle> configBundles = new ArrayList<ConfigBundle>(200);
// If the edited file has locales, then we have to select a matching locale from
// the list.
@@ -816,48 +854,44 @@ public class ConfigurationComposite extends Composite {
bundle.config.setRegionQualifier((RegionQualifier) l[LOCALE_REGION]);
bundle.localeIndex = i;
- addConfig.add(bundle);
+ configBundles.add(bundle);
}
// add the dock mode to the bundle combinations.
- addDockModeToBundles(addConfig);
+ addDockModeToBundles(configBundles);
// add the night mode to the bundle combinations.
- addNightModeToBundles(addConfig);
+ addNightModeToBundles(configBundles);
- mainloop: for (LayoutDevice device : mDeviceList) {
+ for (LayoutDevice device : mDeviceList) {
for (DeviceConfig config : device.getConfigs()) {
- // loop on the list of qualifier adds
- for (ConfigBundle bundle : addConfig) {
- // set the base config. This erase all data in testConfig.
+ // loop on the list of config bundles to create full configurations.
+ for (ConfigBundle bundle : configBundles) {
+ // create a new config with device config
+ FolderConfiguration testConfig = new FolderConfiguration();
testConfig.set(config.getConfig());
- // add on top of it, the extra qualifiers
+ // add on top of it, the extra qualifiers from the bundle
testConfig.add(bundle.config);
if (mEditedConfig.isMatchFor(testConfig)) {
// this is a basic match. record it in case we don't find a match
// where the edited file is a best config.
- if (anyDeviceMatch == null) {
- anyDeviceMatch = device;
- anyConfigMatchName = config.getName();
- anyConfigBundle = bundle;
- }
+ anyMatches.add(new ConfigMatch(testConfig, device, config.getName(),
+ bundle));
if (isCurrentFileBestMatchFor(testConfig)) {
// this is what we want.
- bestDeviceMatch = device;
- bestConfigMatchName = config.getName();
- bestConfigBundle = bundle;
- break mainloop;
+ bestMatches.add(new ConfigMatch(testConfig, device, config.getName(),
+ bundle));
}
}
}
}
}
- if (bestDeviceMatch == null) {
+ if (bestMatches.size() == 0) {
if (favorCurrentConfig) {
// quick check
if (mEditedConfig.isMatchFor(mCurrentConfig) == false) {
@@ -873,13 +907,14 @@ public class ConfigurationComposite extends Composite {
String.format(
"Displaying it with '%1$s'",
mCurrentConfig.toDisplayString()));
- } else if (anyDeviceMatch != null) {
- // select the device anyway.
- selectDevice(mState.device = anyDeviceMatch);
- fillConfigCombo(anyConfigMatchName);
- mLocaleCombo.select(anyConfigBundle.localeIndex);
- mDockCombo.select(anyConfigBundle.dockModeIndex);
- mNightCombo.select(anyConfigBundle.nightModeIndex);
+ } else if (anyMatches.size() > 0) {
+ // select the best device anyway.
+ ConfigMatch match = selectConfigMatch(anyMatches);
+ selectDevice(mState.device = match.device);
+ fillConfigCombo(match.name);
+ mLocaleCombo.select(match.bundle.localeIndex);
+ mDockCombo.select(match.bundle.dockModeIndex);
+ mNightCombo.select(match.bundle.nightModeIndex);
// TODO: display a better warning!
computeCurrentConfig();
@@ -897,14 +932,123 @@ public class ConfigurationComposite extends Composite {
// and replace whatever qualifier required by the layout file.
}
} else {
- selectDevice(mState.device = bestDeviceMatch);
- fillConfigCombo(bestConfigMatchName);
- mLocaleCombo.select(bestConfigBundle.localeIndex);
- mDockCombo.select(bestConfigBundle.dockModeIndex);
- mNightCombo.select(bestConfigBundle.nightModeIndex);
+ ConfigMatch match = selectConfigMatch(bestMatches);
+ selectDevice(mState.device = match.device);
+ fillConfigCombo(match.name);
+ mLocaleCombo.select(match.bundle.localeIndex);
+ mDockCombo.select(match.bundle.dockModeIndex);
+ mNightCombo.select(match.bundle.nightModeIndex);
}
}
+ /**
+ * Note: this comparator imposes orderings that are inconsistent with equals.
+ */
+ private static class TabletConfigComparator implements Comparator<ConfigMatch> {
+ public int compare(ConfigMatch o1, ConfigMatch o2) {
+ ScreenSize ss1 = o1.testConfig.getScreenSizeQualifier().getValue();
+ ScreenSize ss2 = o2.testConfig.getScreenSizeQualifier().getValue();
+
+ // X-LARGE is better than all others (which are considered identical)
+ // if both X-LARGE, then LANDSCAPE is better than all others (which are identical)
+
+ if (ss1 == ScreenSize.XLARGE) {
+ if (ss2 == ScreenSize.XLARGE) {
+ ScreenOrientation so1 =
+ o1.testConfig.getScreenOrientationQualifier().getValue();
+ ScreenOrientation so2 =
+ o2.testConfig.getScreenOrientationQualifier().getValue();
+
+ if (so1 == ScreenOrientation.LANDSCAPE) {
+ if (so2 == ScreenOrientation.LANDSCAPE) {
+ return 0;
+ } else {
+ return -1;
+ }
+ } else if (so2 == ScreenOrientation.LANDSCAPE) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return -1;
+ }
+ } else if (ss2 == ScreenSize.XLARGE) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ /**
+ * Note: this comparator imposes orderings that are inconsistent with equals.
+ */
+ private static class PhoneConfigComparator implements Comparator<ConfigMatch> {
+
+ private SparseIntArray mDensitySort = new SparseIntArray(4);
+
+ public PhoneConfigComparator() {
+ // put the sort order for the density.
+ mDensitySort.put(Density.HIGH.getDpiValue(), 1);
+ mDensitySort.put(Density.MEDIUM.getDpiValue(), 2);
+ mDensitySort.put(Density.XHIGH.getDpiValue(), 3);
+ mDensitySort.put(Density.LOW.getDpiValue(), 4);
+ }
+
+ public int compare(ConfigMatch o1, ConfigMatch o2) {
+ int dpi1 = Density.DEFAULT_DENSITY;
+ if (o1.testConfig.getPixelDensityQualifier() != null) {
+ dpi1 = o1.testConfig.getPixelDensityQualifier().getValue().getDpiValue();
+ dpi1 = mDensitySort.get(dpi1, 100 /* valueIfKeyNotFound*/);
+ }
+
+ int dpi2 = Density.DEFAULT_DENSITY;
+ if (o2.testConfig.getPixelDensityQualifier() != null) {
+ dpi2 = o2.testConfig.getPixelDensityQualifier().getValue().getDpiValue();
+ dpi2 = mDensitySort.get(dpi2, 100 /* valueIfKeyNotFound*/);
+ }
+
+ if (dpi1 == dpi2) {
+ // portrait is better
+ ScreenOrientation so1 =
+ o1.testConfig.getScreenOrientationQualifier().getValue();
+ ScreenOrientation so2 =
+ o2.testConfig.getScreenOrientationQualifier().getValue();
+
+ if (so1 == ScreenOrientation.PORTRAIT) {
+ if (so2 == ScreenOrientation.PORTRAIT) {
+ return 0;
+ } else {
+ return -1;
+ }
+ } else if (so2 == ScreenOrientation.PORTRAIT) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ return dpi1 - dpi2;
+ }
+ }
+
+ private ConfigMatch selectConfigMatch(List<ConfigMatch> matches) {
+ // API 11+ : look for a x-large device.
+ if (mProjectTarget.getVersion().getApiLevel() >= 11) {
+ // TODO: Revisit this once phones can run higher APIs.
+ // Maybe check the compatible-screen tag in the manifest to figure out what kind of
+ // device should be used for display.
+ Collections.sort(matches, new TabletConfigComparator());
+ } else {
+ // lets look for a high density device
+ Collections.sort(matches, new PhoneConfigComparator());
+ }
+
+ // the list has been sorted so that the first item is the best config
+ return matches.get(0);
+ }
+
private void addDockModeToBundles(List<ConfigBundle> addConfig) {
ArrayList<ConfigBundle> list = new ArrayList<ConfigBundle>();
@@ -1008,8 +1152,11 @@ public class ConfigurationComposite extends Composite {
* Finds a locale matching the config from a file.
* @param language the language qualifier or null if none is set.
* @param region the region qualifier or null if none is set.
+ * @return true if there was a change in the combobox as a result of applying the locale
*/
- private void setLocaleCombo(ResourceQualifier language, ResourceQualifier region) {
+ private boolean setLocaleCombo(ResourceQualifier language, ResourceQualifier region) {
+ boolean changed = false;
+
// find the locale match. Since the locale list is based on the content of the
// project resources there must be an exact match.
// The only trick is that the region could be null in the fileConfig but in our
@@ -1026,16 +1173,24 @@ public class ConfigurationComposite extends Composite {
if (RegionQualifier.FAKE_REGION_VALUE.equals(
((RegionQualifier)locale[LOCALE_REGION]).getValue())) {
// match!
- mLocaleCombo.select(i);
+ if (mLocaleCombo.getSelectionIndex() != i) {
+ mLocaleCombo.select(i);
+ changed = true;
+ }
break;
}
} else if (region.equals(locale[LOCALE_REGION])) {
// match!
- mLocaleCombo.select(i);
+ if (mLocaleCombo.getSelectionIndex() != i) {
+ mLocaleCombo.select(i);
+ changed = true;
+ }
break;
}
}
}
+
+ return changed;
}
private void updateConfigDisplay(FolderConfiguration fileConfig) {
@@ -1111,12 +1266,12 @@ public class ConfigurationComposite extends Composite {
boolean hasLocale = false;
// get the languages from the project.
- ProjectResources project = mListener.getProjectResources();
+ ResourceRepository projectRes = mListener.getProjectResources();
// in cases where the opened file is not linked to a project, this could be null.
- if (project != null) {
+ if (projectRes != null) {
// now get the languages from the project.
- languages = project.getLanguages();
+ languages = projectRes.getLanguages();
for (String language : languages) {
hasLocale = true;
@@ -1124,7 +1279,7 @@ public class ConfigurationComposite extends Composite {
LanguageQualifier langQual = new LanguageQualifier(language);
// find the matching regions and add them
- SortedSet<String> regions = project.getRegions(language);
+ SortedSet<String> regions = projectRes.getRegions(language);
for (String region : regions) {
mLocaleCombo.add(
String.format("%1$s / %2$s", language, region)); //$NON-NLS-1$
@@ -1215,22 +1370,120 @@ public class ConfigurationComposite extends Composite {
return; // can't do anything w/o it.
}
- ProjectResources frameworkProject = mListener.getFrameworkResources(getRenderingTarget());
+ ResourceRepository frameworkRes = mListener.getFrameworkResources(getRenderingTarget());
mDisableUpdates++;
try {
// Reset the combo
mThemeCombo.removeAll();
- mPlatformThemeCount = 0;
+ mIsProjectTheme.clear();
ArrayList<String> themes = new ArrayList<String>();
+ String includedIn = mListener != null ? mListener.getIncludedWithin() : null;
+
+ // First list any themes that are declared by the manifest
+ if (mEditedFile != null) {
+ IProject project = mEditedFile.getProject();
+ ManifestInfo manifest = ManifestInfo.get(project);
+
+ // Look up the screen size for the current configuration
+ ScreenSize screenSize = null;
+ if (mState.device != null) {
+ List<DeviceConfig> configs = mState.device.getConfigs();
+ for (DeviceConfig config : configs) {
+ ScreenSizeQualifier qualifier =
+ config.getConfig().getScreenSizeQualifier();
+ screenSize = qualifier.getValue();
+ break;
+ }
+ }
+ // Look up the default/fallback theme to use for this project (which
+ // depends on the screen size when no particular theme is specified
+ // in the manifest)
+ String defaultTheme = manifest.getDefaultTheme(mState.target, screenSize);
+
+ Map<String, String> activityThemes = manifest.getActivityThemes();
+ String pkg = manifest.getPackage();
+ String preferred = null;
+ boolean isIncluded = includedIn != null;
+ if (mState.theme == null || isIncluded) {
+ String layoutName = ResourceHelper.getLayoutName(mEditedFile);
+
+ // If we are rendering a layout in included context, pick the theme
+ // from the outer layout instead
+ if (includedIn != null) {
+ layoutName = includedIn;
+ }
+
+ String activity = ManifestInfo.guessActivity(project, layoutName, pkg);
+ if (activity != null) {
+ preferred = activityThemes.get(activity);
+ }
+ if (preferred == null) {
+ preferred = defaultTheme;
+ }
+ String preferredTheme = ResourceHelper.styleToTheme(preferred);
+ if (includedIn == null) {
+ mState.theme = preferredTheme;
+ }
+ boolean isProjectTheme = !preferred.startsWith(PREFIX_ANDROID_STYLE);
+ mThemeCombo.add(preferredTheme);
+ mIsProjectTheme.add(Boolean.valueOf(isProjectTheme));
+
+ mThemeCombo.add(THEME_SEPARATOR);
+ mIsProjectTheme.add(Boolean.FALSE);
+ }
+
+ // Create a sorted list of unique themes referenced in the manifest
+ // (sort alphabetically, but place the preferred theme at the
+ // top of the list)
+ Set<String> themeSet = new HashSet<String>(activityThemes.values());
+ themeSet.add(defaultTheme);
+ List<String> themeList = new ArrayList<String>(themeSet);
+ final String first = preferred;
+ Collections.sort(themeList, new Comparator<String>() {
+ public int compare(String s1, String s2) {
+ if (s1 == first) {
+ return -1;
+ } else if (s1 == first) {
+ return 1;
+ } else {
+ return s1.compareTo(s2);
+ }
+ }
+ });
+
+ if (themeList.size() > 1 ||
+ (themeList.size() == 1 && (preferred == null ||
+ !preferred.equals(themeList.get(0))))) {
+ for (String style : themeList) {
+ String theme = ResourceHelper.styleToTheme(style);
+
+ // Initialize the chosen theme to the first item
+ // in the used theme list (that's what would be chosen
+ // anyway) such that we stop attempting to look up
+ // the associated activity (during initialization,
+ // this method can be called repeatedly.)
+ if (mState.theme == null) {
+ mState.theme = theme;
+ }
+
+ boolean isProjectTheme = !style.startsWith(PREFIX_ANDROID_STYLE);
+ mThemeCombo.add(theme);
+ mIsProjectTheme.add(Boolean.valueOf(isProjectTheme));
+ }
+ mThemeCombo.add(THEME_SEPARATOR);
+ mIsProjectTheme.add(Boolean.FALSE);
+ }
+ }
// get the themes, and languages from the Framework.
- if (frameworkProject != null) {
+ int platformThemeCount = 0;
+ if (frameworkRes != null) {
// get the configured resources for the framework
Map<ResourceType, Map<String, ResourceValue>> frameworResources =
- frameworkProject.getConfiguredResources(getCurrentConfig());
+ frameworkRes.getConfiguredResources(getCurrentConfig());
if (frameworResources != null) {
// get the styles.
@@ -1242,7 +1495,6 @@ public class ConfigurationComposite extends Composite {
String name = value.getName();
if (name.startsWith("Theme.") || name.equals("Theme")) {
themes.add(value.getName());
- mPlatformThemeCount++;
}
}
@@ -1251,17 +1503,18 @@ public class ConfigurationComposite extends Composite {
for (String theme : themes) {
mThemeCombo.add(theme);
+ mIsProjectTheme.add(Boolean.FALSE);
}
- mPlatformThemeCount = themes.size();
+ platformThemeCount = themes.size();
themes.clear();
}
}
// now get the themes and languages from the project.
- ProjectResources project = mListener.getProjectResources();
+ ResourceRepository projectRes = mListener.getProjectResources();
// in cases where the opened file is not linked to a project, this could be null.
- if (project != null) {
+ if (projectRes != null) {
// get the configured resources for the project
Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes =
mListener.getConfiguredProjectResources();
@@ -1281,14 +1534,16 @@ public class ConfigurationComposite extends Composite {
}
// sort them and add them the to the combo.
- if (mPlatformThemeCount > 0 && themes.size() > 0) {
+ if (platformThemeCount > 0 && themes.size() > 0) {
mThemeCombo.add(THEME_SEPARATOR);
+ mIsProjectTheme.add(Boolean.FALSE);
}
Collections.sort(themes);
for (String theme : themes) {
mThemeCombo.add(theme);
+ mIsProjectTheme.add(Boolean.TRUE);
}
}
}
@@ -1297,7 +1552,7 @@ public class ConfigurationComposite extends Composite {
// try to reselect the previous theme.
boolean needDefaultSelection = true;
- if (mState.theme != null) {
+ if (mState.theme != null && includedIn == null) {
final int count = mThemeCombo.getItemCount();
for (int i = 0 ; i < count ; i++) {
if (mState.theme.equals(mThemeCombo.getItem(i))) {
@@ -1322,6 +1577,8 @@ public class ConfigurationComposite extends Composite {
} finally {
mDisableUpdates--;
}
+
+ assert mIsProjectTheme.size() == mThemeCombo.getItemCount();
}
// ---- getters for the config selection values ----
@@ -1451,7 +1708,7 @@ public class ConfigurationComposite extends Composite {
* @return true for project theme, false for framework theme
*/
public boolean isProjectTheme() {
- return mThemeCombo.getSelectionIndex() >= mPlatformThemeCount;
+ return mIsProjectTheme.get(mThemeCombo.getSelectionIndex()).booleanValue();
}
public IAndroidTarget getRenderingTarget() {
@@ -1770,6 +2027,7 @@ public class ConfigurationComposite extends Composite {
if (computeCurrentConfig() && mListener != null) {
mListener.onConfigurationChange();
+ mListener.onDevicePostChange();
}
}
@@ -1786,6 +2044,9 @@ public class ConfigurationComposite extends Composite {
if (computeCurrentConfig() && mListener != null) {
mListener.onConfigurationChange();
}
+
+ // Store locale project-wide setting
+ saveRenderState();
}
private void onDockChange() {
@@ -1837,6 +2098,9 @@ public class ConfigurationComposite extends Composite {
if (computeOk && mListener != null) {
mListener.onConfigurationChange();
}
+
+ // Store project-wide render-target setting
+ saveRenderState();
}
/**
@@ -1949,9 +2213,9 @@ public class ConfigurationComposite extends Composite {
}
// check for framework identifier.
- if (parentStyle.startsWith("android:")) {
+ if (parentStyle.startsWith(ANDROID_NS_NAME_PREFIX)) {
frameworkStyle = true;
- parentStyle = parentStyle.substring("android:".length());
+ parentStyle = parentStyle.substring(ANDROID_NS_NAME_PREFIX.length());
}
// at this point we could have the format style/<name>. we want only the name
@@ -2028,5 +2292,169 @@ public class ConfigurationComposite extends Composite {
mEditedConfig = null;
onXmlModelLoaded();
}
-}
+ /**
+ * Syncs this configuration to the project wide locale and render target settings. The
+ * locale may ignore the project-wide setting if it is a locale-specific
+ * configuration.
+ *
+ * @return true if one or both of the toggles were changed, false if there were no
+ * changes
+ */
+ public boolean syncRenderState() {
+ if (mEditedConfig == null) {
+ // Startup; ignore
+ return false;
+ }
+
+ boolean localeChanged = false;
+ boolean renderTargetChanged = false;
+
+ // When a page is re-activated, force the toggles to reflect the current project
+ // state
+
+ Pair<ResourceQualifier[], IAndroidTarget> pair = loadRenderState();
+
+ // Only sync the locale if this layout is not already a locale-specific layout!
+ if (!isLocaleSpecificLayout()) {
+ ResourceQualifier[] locale = pair.getFirst();
+ if (locale != null) {
+ localeChanged = setLocaleCombo(locale[0], locale[1]);
+ }
+ }
+
+ // Sync render target
+ IAndroidTarget target = pair.getSecond();
+ if (target != null) {
+ int targetIndex = mTargetList.indexOf(target);
+ if (targetIndex != mTargetCombo.getSelectionIndex()) {
+ mTargetCombo.select(targetIndex);
+ renderTargetChanged = true;
+ }
+ }
+
+ if (!renderTargetChanged && !localeChanged) {
+ return false;
+ }
+
+ // Update the locale and/or the render target. This code contains a logical
+ // merge of the onRenderingTargetChange() and onLocaleChange() methods, combined
+ // such that we don't duplicate work.
+
+ if (renderTargetChanged) {
+ if (mListener != null && mRenderingTarget != null) {
+ mListener.onRenderingTargetPreChange(mRenderingTarget);
+ }
+ int targetIndex = mTargetCombo.getSelectionIndex();
+ mRenderingTarget = mTargetList.get(targetIndex);
+ }
+
+ // Compute the new configuration; we want to do this both for locale changes
+ // and for render targets.
+ boolean computeOk = computeCurrentConfig();
+
+ if (renderTargetChanged) {
+ // force a theme update to reflect the new rendering target.
+ // This must be done after computeCurrentConfig since it'll depend on the currentConfig
+ // to figure out the theme list.
+ updateThemes();
+
+ if (mListener != null && mRenderingTarget != null) {
+ mListener.onRenderingTargetPostChange(mRenderingTarget);
+ }
+ }
+
+ // For both locale and render target changes
+ if (computeOk && mListener != null) {
+ mListener.onConfigurationChange();
+ }
+
+ return true;
+ }
+
+ /**
+ * Loads the render state (the locale and the render target, which are shared among
+ * all the layouts meaning that changing it in one will change it in all) and returns
+ * the current project-wide locale and render target to be used.
+ *
+ * @return a pair of locale resource qualifiers and render target
+ */
+ private Pair<ResourceQualifier[], IAndroidTarget> loadRenderState() {
+ IProject project = mEditedFile.getProject();
+ try {
+ String data = project.getPersistentProperty(NAME_RENDER_STATE);
+ if (data != null) {
+ ResourceQualifier[] locale = null;
+ IAndroidTarget target = null;
+
+ String[] values = data.split(SEP);
+ if (values.length == 2) {
+ locale = new ResourceQualifier[2];
+ String locales[] = values[0].split(SEP_LOCALE);
+ if (locales.length >= 2) {
+ if (locales[0].length() > 0) {
+ locale[0] = new LanguageQualifier(locales[0]);
+ }
+ if (locales[1].length() > 0) {
+ locale[1] = new RegionQualifier(locales[1]);
+ }
+ }
+ target = stringToTarget(values[1]);
+ }
+
+ return Pair.of(locale, target);
+ }
+
+ ResourceQualifier[] any = new ResourceQualifier[] {
+ new LanguageQualifier(LanguageQualifier.FAKE_LANG_VALUE),
+ new RegionQualifier(RegionQualifier.FAKE_REGION_VALUE)
+ };
+
+ return Pair.of(any, findDefaultRenderTarget());
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+
+ return null;
+ }
+
+ /** Returns true if the current layout is locale-specific */
+ private boolean isLocaleSpecificLayout() {
+ return mEditedConfig == null || mEditedConfig.getLanguageQualifier() != null;
+ }
+
+ /**
+ * Saves the render state (the current locale and render target settings) into the
+ * project wide settings storage
+ */
+ private void saveRenderState() {
+ IProject project = mEditedFile.getProject();
+ try {
+ int index = mLocaleCombo.getSelectionIndex();
+ ResourceQualifier[] locale = mLocaleList.get(index);
+ index = mTargetCombo.getSelectionIndex();
+ IAndroidTarget target = mTargetList.get(index);
+
+ // Generate a persistent string from locale+target
+ StringBuilder sb = new StringBuilder();
+ if (locale != null) {
+ if (locale[0] != null && locale[1] != null) {
+ // locale[0]/[1] can be null sometimes when starting Eclipse
+ sb.append(((LanguageQualifier) locale[0]).getValue());
+ sb.append(SEP_LOCALE);
+ sb.append(((RegionQualifier) locale[1]).getValue());
+ }
+ }
+ sb.append(SEP);
+ if (target != null) {
+ sb.append(targetToString(target));
+ sb.append(SEP);
+ }
+
+ String data = sb.toString();
+ project.setPersistentProperty(NAME_RENDER_STATE, data);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java
index 978f114..b63785c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java
@@ -16,13 +16,13 @@
package com.android.ide.eclipse.adt.internal.editors.layout.configuration;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
-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.ide.eclipse.adt.internal.ui.ConfigurationSelector.SelectorMode;
+import com.android.resources.ResourceFolderType;
import com.android.sdkuilib.ui.GridDialog;
import org.eclipse.jface.dialogs.Dialog;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java
index bfc8bb0..db72ac6 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.descriptors;
import com.android.ide.common.resources.platform.ViewClassInfo;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
@@ -29,6 +30,10 @@ import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.ISharedImages;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
import java.util.HashMap;
import java.util.List;
@@ -157,16 +162,9 @@ public final class CustomViewDescriptorService {
if (parentDescriptor != null) {
// we have a valid parent, lets create a new ViewElementDescriptor.
- ViewElementDescriptor descriptor = new ViewElementDescriptor(fqcn,
- fqcn, // ui_name
- fqcn, // canonical class name
- null, // tooltip
- null, // sdk_url
- getAttributeDescriptor(type, parentDescriptor),
- null, // layout attributes
- null, // children
- false /* mandatory */);
-
+ String name = DescriptorsUtils.getBasename(fqcn);
+ ViewElementDescriptor descriptor = new CustomViewDescriptor(name, fqcn,
+ getAttributeDescriptor(type, parentDescriptor));
descriptor.setSuperClass(parentDescriptor);
synchronized (mCustomDescriptorMap) {
@@ -242,16 +240,9 @@ public final class CustomViewDescriptorService {
if (parentDescriptor != null) {
// parent class is a valid View class with a descriptor, so we create one
// for this class.
- ViewElementDescriptor descriptor = new ViewElementDescriptor(fqcn,
- fqcn, // ui_name
- fqcn, // canonical name
- null, // tooltip
- null, // sdk_url
- getAttributeDescriptor(type, parentDescriptor),
- null, // layout attributes
- null, // children
- false /* mandatory */);
-
+ String name = DescriptorsUtils.getBasename(fqcn);
+ ViewElementDescriptor descriptor = new CustomViewDescriptor(name, fqcn,
+ getAttributeDescriptor(type, parentDescriptor));
descriptor.setSuperClass(parentDescriptor);
// add it to the map
@@ -290,4 +281,31 @@ public final class CustomViewDescriptorService {
// TODO add the class attribute descriptors to the parent descriptors.
return parentDescriptor.getAttributes();
}
+
+ private class CustomViewDescriptor extends ViewElementDescriptor {
+ public CustomViewDescriptor(String name, String fqcn, AttributeDescriptor[] attributes) {
+ super(
+ fqcn, // xml name
+ name, // ui name
+ fqcn, // full class name
+ fqcn, // tooltip
+ null, // sdk_url
+ attributes,
+ null, // layout attributes
+ null, // children
+ false // mandatory
+ );
+ }
+
+ @Override
+ public Image getGenericIcon() {
+ // Java source file icon. We could use the Java class icon here
+ // (IMG_OBJS_CLASS), but it does not work well on anything but
+ // white backgrounds
+ ISharedImages sharedImages = JavaUI.getSharedImages();
+ String key = ISharedImages.IMG_OBJS_CUNIT;
+ ImageDescriptor descriptor = sharedImages.getImageDescriptor(key);
+ return descriptor.createImage();
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java
index b61c069..4613492 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java
@@ -42,20 +42,27 @@ import java.util.Map.Entry;
public final class LayoutDescriptors implements IDescriptorProvider {
/**
- * The XML name of the special &lt;include&gt; layout tag.
+ * The XML name of the special {@code <include>} layout tag.
* A synthetic element with that name is created as part of the view descriptors list
* returned by {@link #getViewDescriptors()}.
*/
public static final String VIEW_INCLUDE = "include"; //$NON-NLS-1$
/**
- * The XML name of the special &lt;merge&gt; layout tag.
+ * The XML name of the special {@code <merge>} layout tag.
* A synthetic element with that name is created as part of the view descriptors list
* returned by {@link #getViewDescriptors()}.
*/
public static final String VIEW_MERGE = "merge"; //$NON-NLS-1$
/**
+ * The XML name of the special {@code <requestFocus>} layout tag.
+ * A synthetic element with that name is created as part of the view descriptors list
+ * returned by {@link #getViewDescriptors()}.
+ */
+ public static final String REQUEST_FOCUS = "requestFocus";//$NON-NLS-1$
+
+ /**
* The attribute name of the include tag's url naming the resource to be inserted
* <p>
* <b>NOTE</b>: The layout attribute is NOT in the Android namespace!
@@ -174,6 +181,10 @@ public final class LayoutDescriptors implements IDescriptorProvider {
fixSuperClasses(infoDescMap);
+ ViewElementDescriptor requestFocus = createRequestFocus();
+ newViews.add(requestFocus);
+ newDescriptors.add(requestFocus);
+
// The <merge> tag can only be a root tag, so it is added at the end.
// It gets everything else as children but it is not made a child itself.
ViewElementDescriptor mergeTag = createMerge(newLayouts);
@@ -342,7 +353,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
}
/**
- * Creates and return a new <merge> descriptor.
+ * Creates and return a new {@code <merge>} descriptor.
* @param knownLayouts A list of all known layout view descriptors, used to find the
* FrameLayout descriptor and extract its layout attributes.
*/
@@ -368,6 +379,27 @@ public final class LayoutDescriptors implements IDescriptorProvider {
}
/**
+ * Creates and return a new {@code <requestFocus>} descriptor.
+ * @param knownLayouts A list of all known layout view descriptors, used to find the
+ * FrameLayout descriptor and extract its layout attributes.
+ */
+ private ViewElementDescriptor createRequestFocus() {
+ String xml_name = REQUEST_FOCUS;
+
+ // Create the include descriptor
+ return new ViewElementDescriptor(
+ xml_name, // xml_name
+ xml_name, // ui_name
+ xml_name, // "class name"; the GLE only treats this as an element tag
+ "Requests focus for the parent element or one of its descendants", // tooltip
+ null, // sdk_url
+ null, // attributes
+ null, // layout attributes
+ null, // children
+ false /* mandatory */);
+ }
+
+ /**
* Finds the descriptor and retrieves all its layout attributes.
*/
private AttributeDescriptor[] findViewLayoutAttributes(
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/ViewElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/ViewElementDescriptor.java
index ca07b7a..b45a197 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/ViewElementDescriptor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/ViewElementDescriptor.java
@@ -16,11 +16,15 @@
package com.android.ide.eclipse.adt.internal.editors.layout.descriptors;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import org.eclipse.swt.graphics.Image;
+
/**
* {@link ViewElementDescriptor} describes the properties expected for a given XML element node
* representing a class in an XML Layout file.
@@ -43,13 +47,13 @@ import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
*
* @see ElementDescriptor
*/
-public final class ViewElementDescriptor extends ElementDescriptor {
+public class ViewElementDescriptor extends ElementDescriptor {
/** The full class name (FQCN) of this view. */
- private String mFullClassName;
+ private final String mFullClassName;
/** The list of layout attributes. Can be empty but not null. */
- private AttributeDescriptor[] mLayoutAttributes;
+ private final AttributeDescriptor[] mLayoutAttributes;
/** The super-class descriptor. Can be null. */
private ViewElementDescriptor mSuperClassDesc;
@@ -96,6 +100,7 @@ public final class ViewElementDescriptor extends ElementDescriptor {
public ViewElementDescriptor(String xml_name, String fullClassName) {
super(xml_name);
mFullClassName = fullClassName;
+ mLayoutAttributes = null;
}
/**
@@ -134,4 +139,33 @@ public final class ViewElementDescriptor extends ElementDescriptor {
public void setSuperClass(ViewElementDescriptor superClassDesc) {
mSuperClassDesc = superClassDesc;
}
+
+ /**
+ * Returns an optional icon for the element.
+ * <p/>
+ * By default this tries to return an icon based on the XML name of the element.
+ * If this fails, it tries to return the default element icon as defined in the
+ * plugin. If all fails, it returns null.
+ *
+ * @return An icon for this element or null.
+ */
+ @Override
+ public Image getGenericIcon() {
+ IconFactory factory = IconFactory.getInstance();
+ String name = mXmlName;
+ if (name.indexOf('.') != -1) {
+ // If the user uses a fully qualified name, such as
+ // "android.gesture.GestureOverlayView" in their XML, we need to look up
+ // only by basename
+ name = name.substring(name.lastIndexOf('.') + 1);
+ }
+
+ Image icon = factory.getIcon(name);
+ if (icon == null) {
+ icon = AdtPlugin.getAndroidLogo();
+ }
+
+ return icon;
+ }
+
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/AccordionControl.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/AccordionControl.java
index 24c582d..3c18b16 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/AccordionControl.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/AccordionControl.java
@@ -82,8 +82,8 @@ public abstract class AccordionControl extends Composite {
* overridden to lay out the children with a different layout than the default
* vertical RowLayout
*/
- protected Composite createChildContainer(Composite parent) {
- Composite composite = new Composite(parent, SWT.NONE);
+ protected Composite createChildContainer(Composite parent, Object header, int style) {
+ Composite composite = new Composite(parent, style);
if (mWrap) {
RowLayout layout = new RowLayout(SWT.HORIZONTAL);
layout.center = true;
@@ -335,9 +335,12 @@ public abstract class AccordionControl extends Composite {
updateIcon(label);
if (!scrollGridData.exclude && scrolledComposite.getContent() == null) {
- Composite composite = createChildContainer(scrolledComposite);
Object header = getHeader(label);
+ Composite composite = createChildContainer(scrolledComposite, header, SWT.NONE);
createChildren(composite, header);
+ while (composite.getParent() != scrolledComposite) {
+ composite = composite.getParent();
+ }
scrolledComposite.setContent(composite);
scrolledComposite.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
index 6c32445..2a70dd0 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
+import static com.android.ide.common.layout.LayoutConstants.GESTURE_OVERLAY_VIEW;
import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE;
import com.android.ide.common.api.Rect;
@@ -352,7 +353,7 @@ public class CanvasViewInfo implements IPropertySource {
// root as well (such that the whole layout canvas does not highlight as part of hovers
// etc)
if (mParent != null
- && mParent.mName.endsWith("GestureOverlayView") //$NON-NLS-1$
+ && mParent.mName.endsWith(GESTURE_OVERLAY_VIEW)
&& mParent.isRoot()) {
return true;
}
@@ -362,16 +363,16 @@ public class CanvasViewInfo implements IPropertySource {
}
/**
- * Returns true if this {@link CanvasViewInfo} represents an invisible parent - in
- * other words, a view that can have children, and that has zero bounds making it
- * effectively invisible. (We don't actually look for -0- bounds, but
- * bounds smaller than SELECTION_MIN_SIZE.)
+ * Returns true if this {@link CanvasViewInfo} represents an invisible widget that
+ * should be highlighted when selected. This is the case for any layout that is less than the minimum
+ * threshold ({@link #SELECTION_MIN_SIZE}), or any other view that has -0- bounds.
*
- * @return True if this is an invisible parent.
+ * @return True if this is a tiny layout or invisible view
*/
- public boolean isInvisibleParent() {
+ public boolean isInvisible() {
if (mAbsRect.width < SELECTION_MIN_SIZE || mAbsRect.height < SELECTION_MIN_SIZE) {
- return mUiViewNode != null && mUiViewNode.getDescriptor().hasChildren();
+ return mUiViewNode != null && (mUiViewNode.getDescriptor().hasChildren() ||
+ mAbsRect.width <= 0 || mAbsRect.height <= 0);
}
return false;
@@ -382,7 +383,7 @@ public class CanvasViewInfo implements IPropertySource {
* make it visible during selection or dragging? Note that this is NOT considered to
* be the case in the explode-all-views mode where all nodes have their padding
* increased; it's only used for views that individually exploded because they were
- * requested visible and they returned true for {@link #isInvisibleParent()}.
+ * requested visible and they returned true for {@link #isInvisible()}.
*
* @return True if this is an exploded node.
*/
@@ -525,19 +526,41 @@ public class CanvasViewInfo implements IPropertySource {
* This method will build up a set of {@link CanvasViewInfo} that corresponds to the
* actual <b>selectable</b> views (which are also shown in the Outline).
*
+ * @param layoutlib5 if true, the {@link ViewInfo} hierarchy was created by layoutlib
+ * version 5 or higher, which means this algorithm can make certain assumptions
+ * (for example that {@code <merge>} siblings will provide {@link MergeCookie}
+ * references, so we don't have to search for them.)
* @param root the root {@link ViewInfo} to build from
* @return a {@link CanvasViewInfo} hierarchy
*/
- public static Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root) {
- return new Builder().create(root);
+ public static Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root, boolean layoutlib5) {
+ return new Builder(layoutlib5).create(root);
}
/** Builder object which walks over a tree of {@link ViewInfo} objects and builds
* up a corresponding {@link CanvasViewInfo} hierarchy. */
private static class Builder {
- private Map<UiViewElementNode,List<CanvasViewInfo>> mMergeNodeMap;
+ public Builder(boolean layoutlib5) {
+ mLayoutLib5 = layoutlib5;
+ }
- public Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root) {
+ /**
+ * The mapping from nodes that have a {@code <merge>} as a parent in the node
+ * model to their corresponding views
+ */
+ private Map<UiViewElementNode, List<CanvasViewInfo>> mMergeNodeMap;
+
+ /**
+ * Whether the ViewInfos are provided by a layout library that is version 5 or
+ * later, since that will allow us to take several shortcuts
+ */
+ private boolean mLayoutLib5;
+
+ /**
+ * Creates a hierarchy of {@link CanvasViewInfo} objects and merge bounding
+ * rectangles from the given {@link ViewInfo} hierarchy
+ */
+ private Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root) {
Object cookie = root.getCookie();
if (cookie == null) {
// Special case: If the root-most view does not have a view cookie,
@@ -716,10 +739,24 @@ public class CanvasViewInfo implements IPropertySource {
parentX += viewInfo.getLeft();
parentY += viewInfo.getTop();
+ List<ViewInfo> children = viewInfo.getChildren();
+
+ if (mLayoutLib5) {
+ for (ViewInfo child : children) {
+ Object cookie = child.getCookie();
+ if (cookie instanceof UiViewElementNode || cookie instanceof MergeCookie) {
+ CanvasViewInfo childView = createSubtree(view, child,
+ parentX, parentY);
+ view.addChild(childView);
+ } // else: null cookies, adapter item references, etc: No child views.
+ }
+
+ return view;
+ }
+
// See if we have any missing keys at this level
int missingNodes = 0;
int mergeNodes = 0;
- List<ViewInfo> children = viewInfo.getChildren();
for (ViewInfo child : children) {
// Only use children which have a ViewKey of the correct type.
// We can't interact with those when they have a null key or
@@ -750,7 +787,9 @@ public class CanvasViewInfo implements IPropertySource {
// embedded_layout rendering, or we are including a view with a <merge>
// as the root element.
- String containerName = view.getUiViewNode().getDescriptor().getXmlLocalName();
+ UiViewElementNode uiViewNode = view.getUiViewNode();
+ String containerName = uiViewNode != null
+ ? uiViewNode.getDescriptor().getXmlLocalName() : ""; //$NON-NLS-1$
if (containerName.equals(LayoutDescriptors.VIEW_INCLUDE)) {
// This is expected -- we don't WANT to get node keys for the content
// of an include since it's in a different file and should be treated
@@ -763,9 +802,11 @@ public class CanvasViewInfo implements IPropertySource {
// that there are <merge> tags which are doing surprising things
// to the view hierarchy
LinkedList<UiViewElementNode> unused = new LinkedList<UiViewElementNode>();
- for (UiElementNode child : view.getUiViewNode().getUiChildren()) {
- if (child instanceof UiViewElementNode) {
- unused.addLast((UiViewElementNode) child);
+ if (uiViewNode != null) {
+ for (UiElementNode child : uiViewNode.getUiChildren()) {
+ if (child instanceof UiViewElementNode) {
+ unused.addLast((UiViewElementNode) child);
+ }
}
}
for (ViewInfo child : children) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPoint.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPoint.java
index e90371f..ba09220 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPoint.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPoint.java
@@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
@@ -57,6 +58,20 @@ public final class ControlPoint {
}
/**
+ * Constructs a new {@link ControlPoint} from the given menu detect event.
+ *
+ * @param canvas The {@link LayoutCanvas} this point is within.
+ * @param event The menu detect event to construct the {@link ControlPoint} from.
+ * @return A {@link ControlPoint} which corresponds to the given
+ * {@link MenuDetectEvent}.
+ */
+ public static ControlPoint create(LayoutCanvas canvas, MenuDetectEvent event) {
+ // The menu detect events are always display-relative.
+ org.eclipse.swt.graphics.Point p = canvas.toControl(event.x, event.y);
+ return new ControlPoint(canvas, p.x, p.y);
+ }
+
+ /**
* Constructs a new {@link ControlPoint} from the given event. The event
* must be from a {@link DragSourceListener} associated with the
* {@link LayoutCanvas} such that the {@link DragSourceEvent#x} and
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CustomViewFinder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CustomViewFinder.java
new file mode 100644
index 0000000..3226a02
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CustomViewFinder.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.gle2;
+
+import static com.android.sdklib.SdkConstants.CLASS_VIEW;
+import static com.android.sdklib.SdkConstants.CLASS_VIEWGROUP;
+import static com.android.sdklib.SdkConstants.FN_FRAMEWORK_LIBRARY;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.SearchEngine;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchParticipant;
+import org.eclipse.jdt.core.search.SearchPattern;
+import org.eclipse.jdt.core.search.SearchRequestor;
+import org.eclipse.jdt.internal.core.ResolvedBinaryType;
+import org.eclipse.jdt.internal.core.ResolvedSourceType;
+import org.eclipse.swt.widgets.Display;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The {@link CustomViewFinder} can look up the custom views and third party views
+ * available for a given project.
+ */
+@SuppressWarnings("restriction") // JDT model access for custom-view class lookup
+public class CustomViewFinder {
+ /**
+ * Qualified name for the per-project non-persistent property storing the
+ * {@link CustomViewFinder} for this project
+ */
+ private final static QualifiedName CUSTOM_VIEW_FINDER = new QualifiedName(AdtPlugin.PLUGIN_ID,
+ "viewfinder"); //$NON-NLS-1$
+
+ /** Project that this view finder locates views for */
+ private final IProject mProject;
+
+ private final List<Listener> mListeners = new ArrayList<Listener>();
+
+ private List<String> mCustomViews;
+ private List<String> mThirdPartyViews;
+ private boolean mRefreshing;
+
+ /**
+ * Constructs an {@link CustomViewFinder} for the given project. Don't use this method;
+ * use the {@link #get} factory method instead.
+ *
+ * @param project project to create an {@link CustomViewFinder} for
+ */
+ private CustomViewFinder(IProject project) {
+ mProject = project;
+ }
+
+ /**
+ * Returns the {@link CustomViewFinder} for the given project
+ *
+ * @param project the project the finder is associated with
+ * @return a {@CustomViewFinder} for the given project, never null
+ */
+ public static CustomViewFinder get(IProject project) {
+ CustomViewFinder finder = null;
+ try {
+ finder = (CustomViewFinder) project.getSessionProperty(CUSTOM_VIEW_FINDER);
+ } catch (CoreException e) {
+ // Not a problem; we will just create a new one
+ }
+
+ if (finder == null) {
+ finder = new CustomViewFinder(project);
+ try {
+ project.setSessionProperty(CUSTOM_VIEW_FINDER, finder);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, "Can't store CustomViewFinder");
+ }
+ }
+
+ return finder;
+ }
+
+ public void refresh() {
+ refresh(null);
+ }
+
+ public void refresh(final Listener listener) {
+ // Add this listener to the list of listeners which should be notified when the
+ // search is done. (There could be more than one since multiple requests could
+ // arrive for a slow search since the search is run in a different thread).
+ if (listener != null) {
+ synchronized (this) {
+ mListeners.add(listener);
+ }
+ }
+ synchronized (this) {
+ if (listener != null) {
+ mListeners.add(listener);
+ }
+ if (mRefreshing) {
+ return;
+ }
+ mRefreshing = true;
+ }
+
+ FindViewsJob job = new FindViewsJob();
+ job.schedule();
+ }
+
+ public Collection<String> getCustomViews() {
+ return mCustomViews == null ? null : Collections.unmodifiableCollection(mCustomViews);
+ }
+
+ public Collection<String> getThirdPartyViews() {
+ return mThirdPartyViews == null
+ ? null : Collections.unmodifiableCollection(mThirdPartyViews);
+ }
+
+ public Collection<String> getAllViews() {
+ // Not yet initialized: return null
+ if (mCustomViews == null) {
+ return null;
+ }
+ List<String> all = new ArrayList<String>(mCustomViews.size() + mThirdPartyViews.size());
+ all.addAll(mCustomViews);
+ all.addAll(mThirdPartyViews);
+ return all;
+ }
+
+ /**
+ * Returns a pair of view lists - the custom views and the 3rd-party views.
+ * This method performs no caching; it is the same as asking the custom view finder
+ * to refresh itself and then waiting for the answer and returning it.
+ *
+ * @param project the Android project
+ * @param layoutsOnly if true, only search for layouts
+ * @return a pair of lists, the first containing custom views and the second
+ * containing 3rd party views
+ */
+ public static Pair<List<String>,List<String>> findViews(
+ final IProject project, boolean layoutsOnly) {
+ CustomViewFinder finder = get(project);
+
+ return finder.findViews(layoutsOnly);
+ }
+
+ private Pair<List<String>,List<String>> findViews(final boolean layoutsOnly) {
+ final List<String> customViews = new ArrayList<String>();
+ final List<String> thirdPartyViews = new ArrayList<String>();
+
+ ProjectState state = Sdk.getProjectState(mProject);
+ final List<IProject> libraries = state != null
+ ? state.getFullLibraryProjects() : Collections.<IProject>emptyList();
+
+ SearchRequestor requestor = new SearchRequestor() {
+ @Override
+ public void acceptSearchMatch(SearchMatch match) throws CoreException {
+ // Ignore matches in comments
+ if (match.isInsideDocComment()) {
+ return;
+ }
+
+ Object element = match.getElement();
+ if (element instanceof ResolvedBinaryType) {
+ // Third party view
+ ResolvedBinaryType type = (ResolvedBinaryType) element;
+ IPackageFragment fragment = type.getPackageFragment();
+ IPath path = fragment.getPath();
+ String last = path.lastSegment();
+ // Filter out android.jar stuff
+ if (last.equals(FN_FRAMEWORK_LIBRARY)) {
+ return;
+ }
+ if (!isValidView(type, layoutsOnly)) {
+ return;
+ }
+
+ IProject matchProject = match.getResource().getProject();
+ if (mProject == matchProject || libraries.contains(matchProject)) {
+ String fqn = type.getFullyQualifiedName();
+ thirdPartyViews.add(fqn);
+ }
+ } else if (element instanceof ResolvedSourceType) {
+ // User custom view
+ IProject matchProject = match.getResource().getProject();
+ if (mProject == matchProject || libraries.contains(matchProject)) {
+ ResolvedSourceType type = (ResolvedSourceType) element;
+ if (!isValidView(type, layoutsOnly)) {
+ return;
+ }
+ String fqn = type.getFullyQualifiedName();
+ fqn = fqn.replace('$', '.');
+ customViews.add(fqn);
+ }
+ }
+ }
+ };
+ try {
+ IJavaProject javaProject = BaseProjectHelper.getJavaProject(mProject);
+ if (javaProject != null) {
+ String className = layoutsOnly ? CLASS_VIEWGROUP : CLASS_VIEW;
+ IType viewType = javaProject.findType(className);
+ if (viewType != null) {
+ IJavaSearchScope scope = SearchEngine.createHierarchyScope(viewType);
+ SearchParticipant[] participants = new SearchParticipant[] {
+ SearchEngine.getDefaultSearchParticipant()
+ };
+ int matchRule = SearchPattern.R_PATTERN_MATCH | SearchPattern.R_CASE_SENSITIVE;
+
+ SearchPattern pattern = SearchPattern.createPattern("*",
+ IJavaSearchConstants.CLASS, IJavaSearchConstants.IMPLEMENTORS,
+ matchRule);
+ SearchEngine engine = new SearchEngine();
+ engine.search(pattern, participants, scope, requestor,
+ new NullProgressMonitor());
+ }
+ }
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+
+ if (!layoutsOnly) {
+ // Update our cached answers (unless we were filtered on only layouts)
+ mCustomViews = customViews;
+ mThirdPartyViews = thirdPartyViews;
+ }
+
+ return Pair.of(customViews, thirdPartyViews);
+ }
+
+ /**
+ * Determines whether the given member is a valid android.view.View to be added to the
+ * list of custom views or third party views. It checks that the view is public and
+ * not abstract for example.
+ */
+ private static boolean isValidView(IType type, boolean layoutsOnly)
+ throws JavaModelException {
+ // Skip anonymous classes
+ if (type.isAnonymous()) {
+ return false;
+ }
+ int flags = type.getFlags();
+ if (Flags.isAbstract(flags) || !Flags.isPublic(flags)) {
+ return false;
+ }
+
+ // TODO: if (layoutsOnly) perhaps try to filter out AdapterViews and other ViewGroups
+ // not willing to accept children via XML
+
+ // See if the class has one of the acceptable constructors
+ // needed for XML instantiation:
+ // View(Context context)
+ // View(Context context, AttributeSet attrs)
+ // View(Context context, AttributeSet attrs, int defStyle)
+ // We don't simply do three direct checks via type.getMethod() because the types
+ // are not resolved, so we don't know for each parameter if we will get the
+ // fully qualified or the unqualified class names.
+ // Instead, iterate over the methods and look for a match.
+ String typeName = type.getElementName();
+ for (IMethod method : type.getMethods()) {
+ // Only care about constructors
+ if (!method.getElementName().equals(typeName)) {
+ continue;
+ }
+
+ String[] parameterTypes = method.getParameterTypes();
+ if (parameterTypes == null || parameterTypes.length < 1 || parameterTypes.length > 3) {
+ continue;
+ }
+
+ String first = parameterTypes[0];
+ // Look for the parameter type signatures -- produced by
+ // JDT's Signature.createTypeSignature("Context", false /*isResolved*/);.
+ // This is not a typo; they were copy/pasted from the actual parameter names
+ // observed in the debugger examining these data structures.
+ if (first.equals("QContext;") //$NON-NLS-1$
+ || first.equals("Qandroid.content.Context;")) { //$NON-NLS-1$
+ if (parameterTypes.length == 1) {
+ return true;
+ }
+ String second = parameterTypes[1];
+ if (second.equals("QAttributeSet;") //$NON-NLS-1$
+ || second.equals("Qandroid.util.AttributeSet;")) { //$NON-NLS-1$
+ if (parameterTypes.length == 2) {
+ return true;
+ }
+ String third = parameterTypes[2];
+ if (third.equals("I")) { //$NON-NLS-1$
+ if (parameterTypes.length == 3) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Interface implemented by clients of the {@link CustomViewFinder} to be notified
+ * when a custom view search has completed. Will always be called on the SWT event
+ * dispatch thread.
+ */
+ public interface Listener {
+ void viewsUpdated(Collection<String> customViews, Collection<String> thirdPartyViews);
+ }
+
+ /**
+ * Job for performing class search off the UI thread. This is marked as a system job
+ * so that it won't show up in the progress monitor etc.
+ */
+ private class FindViewsJob extends Job {
+ FindViewsJob() {
+ super("Find Custom Views");
+ setSystem(true);
+ }
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ Pair<List<String>, List<String>> views = findViews(false);
+ mCustomViews = views.getFirst();
+ mThirdPartyViews = views.getSecond();
+
+ // Notify listeners on SWT's UI thread
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ Collection<String> customViews =
+ Collections.unmodifiableCollection(mCustomViews);
+ Collection<String> thirdPartyViews =
+ Collections.unmodifiableCollection(mThirdPartyViews);
+ synchronized (this) {
+ for (Listener l : mListeners) {
+ l.viewsUpdated(customViews, thirdPartyViews);
+ }
+ mListeners.clear();
+ mRefreshing = false;
+ }
+ }
+ });
+ return Status.OK_STATUS;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java
new file mode 100644
index 0000000..64ea86a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.gle2;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
+
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.util.Pair;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressWarnings("restriction") // No replacement for restricted XML model yet
+public class DomUtilities {
+ /**
+ * Returns the XML DOM node corresponding to the given offset of the given
+ * document.
+ *
+ * @param document The document to look in
+ * @param offset The offset to look up the node for
+ * @return The node containing the offset, or null
+ */
+ public static Node getNode(IDocument document, int offset) {
+ Node node = null;
+ IModelManager modelManager = StructuredModelManager.getModelManager();
+ if (modelManager == null) {
+ return null;
+ }
+ try {
+ IStructuredModel model = modelManager.getExistingModelForRead(document);
+ if (model != null) {
+ try {
+ for (; offset >= 0 && node == null; --offset) {
+ node = (Node) model.getIndexedRegion(offset);
+ }
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+ } catch (Exception e) {
+ // Ignore exceptions.
+ }
+
+ return node;
+ }
+
+ /**
+ * Returns the editing context at the given offset, as a pair of parent node and child
+ * node. This is not the same as just calling {@link DomUtilities#getNode} and taking
+ * its parent node, because special care has to be taken to return content element
+ * positions.
+ * <p>
+ * For example, for the XML {@code <foo>^</foo>}, if the caret ^ is inside the foo
+ * element, between the opening and closing tags, then the foo element is the parent,
+ * and the child is null which represents a potential text node.
+ * <p>
+ * If the node is inside an element tag definition (between the opening and closing
+ * bracket) then the child node will be the element and whatever parent (element or
+ * document) will be its parent.
+ * <p>
+ * If the node is in a text node, then the text node will be the child and its parent
+ * element or document node its parent.
+ * <p>
+ * Finally, if the caret is on a boundary of a text node, then the text node will be
+ * considered the child, regardless of whether it is on the left or right of the
+ * caret. For example, in the XML {@code <foo>^ </foo>} and in the XML
+ * {@code <foo> ^</foo>}, in both cases the text node is preferred over the element.
+ *
+ * @param document the document to search in
+ * @param offset the offset to look up
+ * @return a pair of parent and child elements, where either the parent or the child
+ * but not both can be null, and if non null the child.getParentNode() should
+ * return the parent. Note that the method can also return null if no
+ * document or model could be obtained or if the offset is invalid.
+ */
+ public static Pair<Node, Node> getNodeContext(IDocument document, int offset) {
+ Node node = null;
+ IModelManager modelManager = StructuredModelManager.getModelManager();
+ if (modelManager == null) {
+ return null;
+ }
+ try {
+ IStructuredModel model = modelManager.getExistingModelForRead(document);
+ if (model != null) {
+ try {
+ for (; offset >= 0 && node == null; --offset) {
+ IndexedRegion indexedRegion = model.getIndexedRegion(offset);
+ if (indexedRegion != null) {
+ node = (Node) indexedRegion;
+
+ if (node.getNodeType() == Node.TEXT_NODE) {
+ return Pair.of(node.getParentNode(), node);
+ }
+
+ // Look at the structured document to see if
+ // we have the special case where the caret is pointing at
+ // a -potential- text node, e.g. <foo>^</foo>
+ IStructuredDocument doc = model.getStructuredDocument();
+ IStructuredDocumentRegion region =
+ doc.getRegionAtCharacterOffset(offset);
+
+ ITextRegion subRegion = region.getRegionAtCharacterOffset(offset);
+ String type = subRegion.getType();
+ if (DOMRegionContext.XML_END_TAG_OPEN.equals(type)) {
+ // Try to return the text node if it's on the left
+ // of this element node, such that replace strings etc
+ // can be computed.
+ Node lastChild = node.getLastChild();
+ if (lastChild != null) {
+ IndexedRegion previousRegion = (IndexedRegion) lastChild;
+ if (previousRegion.getEndOffset() == offset) {
+ return Pair.of(node, lastChild);
+ }
+ }
+ return Pair.of(node, null);
+ }
+
+ return Pair.of(node.getParentNode(), node);
+ }
+ }
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+ } catch (Exception e) {
+ // Ignore exceptions.
+ }
+
+ return null;
+ }
+
+ /**
+ * Like {@link #getNode(IDocument, int)}, but has a bias parameter which lets you
+ * indicate whether you want the search to look forwards or backwards.
+ * This is vital when trying to compute a node range. Consider the following
+ * XML fragment:
+ * {@code
+ * <a/><b/>[<c/><d/><e/>]<f/><g/>
+ * }
+ * Suppose we want to locate the nodes in the range indicated by the brackets above.
+ * If we want to search for the node corresponding to the start position, should
+ * we pick the node on its left or the node on its right? Similarly for the end
+ * position. Clearly, we'll need to bias the search towards the right when looking
+ * for the start position, and towards the left when looking for the end position.
+ * The following method lets us do just that. When passed an offset which sits
+ * on the edge of the computed node, it will pick the neighbor based on whether
+ * "forward" is true or false, where forward means searching towards the right
+ * and not forward is obviously towards the left.
+ * @param document the document to search in
+ * @param offset the offset to search for
+ * @param forward if true, search forwards, otherwise search backwards when on node boundaries
+ * @return the node which surrounds the given offset, or the node adjacent to the offset
+ * where the side depends on the forward parameter
+ */
+ public static Node getNode(IDocument document, int offset, boolean forward) {
+ Node node = getNode(document, offset);
+
+ if (node instanceof IndexedRegion) {
+ IndexedRegion region = (IndexedRegion) node;
+
+ if (!forward && offset <= region.getStartOffset()) {
+ Node left = node.getPreviousSibling();
+ if (left == null) {
+ left = node.getParentNode();
+ }
+
+ node = left;
+ } else if (forward && offset >= region.getEndOffset()) {
+ Node right = node.getNextSibling();
+ if (right == null) {
+ right = node.getParentNode();
+ }
+ node = right;
+ }
+ }
+
+ return node;
+ }
+
+ /**
+ * Returns a range of elements for the given caret range. Note that the two elements
+ * may not be at the same level so callers may want to perform additional input
+ * filtering.
+ *
+ * @param document the document to search in
+ * @param beginOffset the beginning offset of the range
+ * @param endOffset the ending offset of the range
+ * @return a pair of begin+end elements, or null
+ */
+ public static Pair<Element, Element> getElementRange(IDocument document, int beginOffset,
+ int endOffset) {
+ Element beginElement = null;
+ Element endElement = null;
+ Node beginNode = getNode(document, beginOffset, true);
+ Node endNode = beginNode;
+ if (endOffset > beginOffset) {
+ endNode = getNode(document, endOffset, false);
+ }
+
+ if (beginNode == null || endNode == null) {
+ return null;
+ }
+
+ // Adjust offsets if you're pointing at text
+ if (beginNode.getNodeType() != Node.ELEMENT_NODE) {
+ // <foo> <bar1/> | <bar2/> </foo> => should pick <bar2/>
+ beginElement = getNextElement(beginNode);
+ if (beginElement == null) {
+ // Might be inside the end of a parent, e.g.
+ // <foo> <bar/> | </foo> => should pick <bar/>
+ beginElement = getPreviousElement(beginNode);
+ if (beginElement == null) {
+ // We must be inside an empty element,
+ // <foo> | </foo>
+ // In that case just pick the parent.
+ beginElement = getParentElement(beginNode);
+ }
+ }
+ } else {
+ beginElement = (Element) beginNode;
+ }
+
+ if (endNode.getNodeType() != Node.ELEMENT_NODE) {
+ // In the following, | marks the caret position:
+ // <foo> <bar1/> | <bar2/> </foo> => should pick <bar1/>
+ endElement = getPreviousElement(endNode);
+ if (endElement == null) {
+ // Might be inside the beginning of a parent, e.g.
+ // <foo> | <bar/></foo> => should pick <bar/>
+ endElement = getNextElement(endNode);
+ if (endElement == null) {
+ // We must be inside an empty element,
+ // <foo> | </foo>
+ // In that case just pick the parent.
+ endElement = getParentElement(endNode);
+ }
+ }
+ } else {
+ endElement = (Element) endNode;
+ }
+
+ if (beginElement != null && endElement != null) {
+ return Pair.of(beginElement, endElement);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the next sibling element of the node, or null if there is no such element
+ *
+ * @param node the starting node
+ * @return the next sibling element, or null
+ */
+ public static Element getNextElement(Node node) {
+ while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {
+ node = node.getNextSibling();
+ }
+
+ return (Element) node; // may be null as well
+ }
+
+ /**
+ * Returns the previous sibling element of the node, or null if there is no such element
+ *
+ * @param node the starting node
+ * @return the previous sibling element, or null
+ */
+ public static Element getPreviousElement(Node node) {
+ while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {
+ node = node.getPreviousSibling();
+ }
+
+ return (Element) node; // may be null as well
+ }
+
+ /**
+ * Returns the closest ancestor element, or null if none
+ *
+ * @param node the starting node
+ * @return the closest parent element, or null
+ */
+ public static Element getParentElement(Node node) {
+ while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {
+ node = node.getParentNode();
+ }
+
+ return (Element) node; // may be null as well
+ }
+
+ /**
+ * Converts the given attribute value to an XML-attribute-safe value, meaning that
+ * single and double quotes are replaced with their corresponding XML entities.
+ *
+ * @param attrValue the value to be escaped
+ * @return the escaped value
+ */
+ public static String toXmlAttributeValue(String attrValue) {
+ // Must escape ' and "
+ if (attrValue.indexOf('"') == -1 && attrValue.indexOf('\'') == -1) {
+ return attrValue;
+ }
+
+ int n = attrValue.length();
+ StringBuilder sb = new StringBuilder(2 * n);
+ for (int i = 0; i < n; i++) {
+ char c = attrValue.charAt(i);
+ if (c == '"') {
+ sb.append("&quot;"); //$NON-NLS-1$
+ } else if (c == '\'') {
+ sb.append("&apos;"); //$NON-NLS-1$
+ } else {
+ sb.append(c);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /** Utility used by {@link #getFreeWidgetId(Element)} */
+ private static void addLowercaseIds(Element root, Set<String> seen) {
+ if (root.hasAttributeNS(ANDROID_URI, ATTR_ID)) {
+ String id = root.getAttributeNS(ANDROID_URI, ATTR_ID);
+ if (id.startsWith(NEW_ID_PREFIX)) {
+ seen.add(id.substring(NEW_ID_PREFIX.length()).toLowerCase());
+ } else if (id.startsWith(ID_PREFIX)) {
+ seen.add(id.substring(ID_PREFIX.length()).toLowerCase());
+ } else {
+ seen.add(id.toLowerCase());
+ }
+ }
+ }
+
+ /**
+ * Returns a suitable new widget id (not including the {@code @id/} prefix) for the
+ * given element, which is guaranteed to be unique in this document
+ *
+ * @param element the element to compute a new widget id for
+ * @param reserved an optional set of extra, "reserved" set of ids that should be
+ * considered taken
+ * @param prefix an optional prefix to use for the generated name, or null to get a
+ * default (which is currently the tag name)
+ * @return a unique id, never null, which does not include the {@code @id/} prefix
+ * @see DescriptorsUtils#getFreeWidgetId
+ */
+ public static String getFreeWidgetId(Element element, Set<String> reserved, String prefix) {
+ Set<String> ids = new HashSet<String>();
+ if (reserved != null) {
+ for (String id : reserved) {
+ ids.add(id.toLowerCase());
+ }
+ }
+ addLowercaseIds(element.getOwnerDocument().getDocumentElement(), ids);
+
+ if (prefix == null) {
+ prefix = DescriptorsUtils.getBasename(element.getTagName());
+ }
+ String generated;
+ int num = 1;
+ do {
+ generated = String.format("%1$s%2$d", prefix, num++); //$NON-NLS-1$
+ } while (ids.contains(generated.toLowerCase()));
+
+ return generated;
+ }
+
+ /**
+ * Returns the element children of the given element
+ *
+ * @param element the parent element
+ * @return a list of child elements, possibly empty but never null
+ */
+ public static List<Element> getChildren(Element element) {
+ // Convenience to avoid lots of ugly DOM access casting
+ NodeList children = element.getChildNodes();
+ // An iterator would have been more natural (to directly drive the child list
+ // iteration) but iterators can't be used in enhanced for loops...
+ List<Element> result = new ArrayList<Element>(children.getLength());
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element child = (Element) node;
+ result.add(child);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns true iff the given elements are contiguous siblings
+ *
+ * @param elements the elements to be tested
+ * @return true if the elements are contiguous siblings with no gaps
+ */
+ public static boolean isContiguous(List<Element> elements) {
+ if (elements.size() > 1) {
+ // All elements must be siblings (e.g. same parent)
+ Node parent = elements.get(0).getParentNode();
+ if (!(parent instanceof Element)) {
+ return false;
+ }
+ for (Element node : elements) {
+ if (parent != node.getParentNode()) {
+ return false;
+ }
+ }
+
+ // Ensure that the siblings are contiguous; no gaps.
+ // If we've selected all the children of the parent then we don't need
+ // to look.
+ List<Element> siblings = DomUtilities.getChildren((Element) parent);
+ if (siblings.size() != elements.size()) {
+ Set<Element> nodeSet = new HashSet<Element>(elements);
+ boolean inRange = false;
+ int remaining = elements.size();
+ for (Element node : siblings) {
+ boolean in = nodeSet.contains(node);
+ if (in) {
+ remaining--;
+ if (remaining == 0) {
+ break;
+ }
+ inRange = true;
+ } else if (inRange) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determines whether two element trees are equivalent. Two element trees are
+ * equivalent if they represent the same DOM structure (elements, attributes, and
+ * children in order). This is almost the same as simply checking whether the String
+ * representations of the two nodes are identical, but this allows for minor
+ * variations that are not semantically significant, such as variations in formatting
+ * or ordering of the element attribute declarations, and the text children are
+ * ignored (this is such that in for example layout where content is only used for
+ * indentation the indentation differences are ignored). Null trees are never equal.
+ *
+ * @param element1 the first element to compare
+ * @param element2 the second element to compare
+ * @return true if the two element hierarchies are logically equal
+ */
+ public static boolean isEquivalent(Element element1, Element element2) {
+ if (element1 == null || element2 == null) {
+ return false;
+ }
+
+ if (!element1.getTagName().equals(element2.getTagName())) {
+ return false;
+ }
+
+ // Check attribute map
+ NamedNodeMap attributes1 = element1.getAttributes();
+ NamedNodeMap attributes2 = element2.getAttributes();
+ if (attributes1.getLength() != attributes2.getLength()) {
+ return false;
+ }
+ if (attributes1.getLength() > 0) {
+ List<Attr> attributeNodes1 = new ArrayList<Attr>();
+ for (int i = 0, n = attributes1.getLength(); i < n; i++) {
+ attributeNodes1.add((Attr) attributes1.item(i));
+ }
+ List<Attr> attributeNodes2 = new ArrayList<Attr>();
+ for (int i = 0, n = attributes2.getLength(); i < n; i++) {
+ attributeNodes2.add((Attr) attributes2.item(i));
+ }
+ Collections.sort(attributeNodes1, ATTRIBUTE_COMPARATOR);
+ Collections.sort(attributeNodes2, ATTRIBUTE_COMPARATOR);
+ for (int i = 0; i < attributeNodes1.size(); i++) {
+ Attr attr1 = attributeNodes1.get(i);
+ Attr attr2 = attributeNodes2.get(i);
+ if (attr1.getLocalName() == null || attr2.getLocalName() == null) {
+ if (!attr1.getName().equals(attr2.getName())) {
+ return false;
+ }
+ } else if (!attr1.getLocalName().equals(attr2.getLocalName())) {
+ return false;
+ }
+ if (!attr1.getValue().equals(attr2.getValue())) {
+ return false;
+ }
+ if (attr1.getNamespaceURI() == null) {
+ if (attr2.getNamespaceURI() != null) {
+ return false;
+ }
+ } else if (attr2.getNamespaceURI() == null) {
+ return false;
+ } else if (!attr1.getNamespaceURI().equals(attr2.getNamespaceURI())) {
+ return false;
+ }
+ }
+ }
+
+ NodeList children1 = element1.getChildNodes();
+ NodeList children2 = element2.getChildNodes();
+ int nextIndex1 = 0;
+ int nextIndex2 = 0;
+ while (true) {
+ while (nextIndex1 < children1.getLength() &&
+ children1.item(nextIndex1).getNodeType() != Node.ELEMENT_NODE) {
+ nextIndex1++;
+ }
+
+ while (nextIndex2 < children2.getLength() &&
+ children2.item(nextIndex2).getNodeType() != Node.ELEMENT_NODE) {
+ nextIndex2++;
+ }
+
+ Element nextElement1 = (Element) (nextIndex1 < children1.getLength()
+ ? children1.item(nextIndex1) : null);
+ Element nextElement2 = (Element) (nextIndex2 < children2.getLength()
+ ? children2.item(nextIndex2) : null);
+ if (nextElement1 == null) {
+ if (nextElement2 == null) {
+ return true;
+ } else {
+ return false;
+ }
+ } else if (nextElement2 == null) {
+ return false;
+ } else if (!isEquivalent(nextElement1, nextElement2)) {
+ return false;
+ }
+ nextIndex1++;
+ nextIndex2++;
+ }
+ }
+
+ /**
+ * Finds the corresponding element in a document to a given element in another
+ * document. Note that this does <b>not</b> do any kind of equivalence check
+ * (see {@link #isEquivalent(Element, Element)}), and currently the search
+ * is only by id; there is no structural search.
+ *
+ * @param element the element to find an equivalent for
+ * @param document the document to search for an equivalent element in
+ * @return an equivalent element, or null
+ */
+ public static Element findCorresponding(Element element, Document document) {
+ // Make sure the method is called correctly -- the element is for a different
+ // document than the one we are searching
+ assert element.getOwnerDocument() != document;
+
+ // First search by id. This allows us to find the corresponding
+ String id = element.getAttributeNS(ANDROID_URI, ATTR_ID);
+ if (id != null && id.length() > 0) {
+ if (id.startsWith(ID_PREFIX)) {
+ id = NEW_ID_PREFIX + id.substring(ID_PREFIX.length());
+ }
+
+ return findCorresponding(document.getDocumentElement(), id);
+ }
+
+ // TODO: Search by structure - look in the document and
+ // find a corresponding element in the same location in the structure,
+ // e.g. 4th child of root, 3rd child, 6th child, then pick node with tag "foo".
+
+ return null;
+ }
+
+ /** Helper method for {@link #findCorresponding(Element, Document)} */
+ private static Element findCorresponding(Element element, String targetId) {
+ String id = element.getAttributeNS(ANDROID_URI, ATTR_ID);
+ if (id != null) { // Work around DOM bug
+ if (id.equals(targetId)) {
+ return element;
+ } else if (id.startsWith(ID_PREFIX)) {
+ id = NEW_ID_PREFIX + id.substring(ID_PREFIX.length());
+ if (id.equals(targetId)) {
+ return element;
+ }
+ }
+ }
+
+ NodeList children = element.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element child = (Element) node;
+ Element match = findCorresponding(child, targetId);
+ if (match != null) {
+ return match;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /** Can be used to sort attributes by name */
+ private static final Comparator<Attr> ATTRIBUTE_COMPARATOR = new Comparator<Attr>() {
+ public int compare(Attr a1, Attr a2) {
+ return a1.getName().compareTo(a2.getName());
+ }
+ };
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
index 5464917..f188e90 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
@@ -16,6 +16,10 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
+import static com.android.ide.common.layout.LayoutConstants.EXPANDABLE_LIST_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_GESTURE_OVERLAY_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.LIST_VIEW;
+
import com.android.ide.common.api.IMenuCallback;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.MenuAction;
@@ -23,6 +27,12 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeLayoutAction;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeViewAction;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractIncludeAction;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractStyleAction;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.WrapInAction;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
@@ -37,8 +47,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.TreeMap;
+import java.util.Map.Entry;
import java.util.regex.Pattern;
/**
@@ -200,18 +210,46 @@ import java.util.regex.Pattern;
}
}
- insertExtractAsInclude(endId);
+ insertListPreviewType(endId);
+ insertVisualRefactorings(endId);
}
- private void insertExtractAsInclude(String endId) {
- // Extract As <include> refactoring.
+ private void insertVisualRefactorings(String endId) {
+ // Extract As <include> refactoring, Wrap In Refactoring, etc.
+ List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
+ if (selection.size() == 0) {
+ return;
+ }
// Only include the menu item if you are not right clicking on a root,
// or on an included view, or on a non-contiguous selection
- IAction extractIncludeAction = new ExtractIncludeAction(mCanvas);
- if (extractIncludeAction.isEnabled()) {
- mMenuManager.insertBefore(endId, new Separator());
- mMenuManager.insertBefore(endId, extractIncludeAction);
- mMenuManager.insertBefore(endId, new Separator());
+ mMenuManager.insertBefore(endId, new Separator());
+ mMenuManager.insertBefore(endId, ExtractIncludeAction.create(mEditor));
+ mMenuManager.insertBefore(endId, ExtractStyleAction.create(mEditor));
+ mMenuManager.insertBefore(endId, WrapInAction.create(mEditor));
+ if (selection.size() == 1 && (selection.get(0).isLayout() ||
+ selection.get(0).getViewInfo().getName().equals(FQCN_GESTURE_OVERLAY_VIEW))) {
+ mMenuManager.insertBefore(endId, ChangeLayoutAction.create(mEditor));
+ } else {
+ mMenuManager.insertBefore(endId, ChangeViewAction.create(mEditor));
+ }
+ mMenuManager.insertBefore(endId, new Separator());
+ }
+
+ /** "Preview List Content" pull-right menu */
+ private void insertListPreviewType(String endId) {
+
+ List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
+ if (selection.size() == 0) {
+ return;
+ }
+ for (SelectionItem item : selection) {
+ UiViewElementNode node = item.getViewInfo().getUiViewNode();
+ String name = node.getDescriptor().getXmlLocalName();
+ if (name.equals(LIST_VIEW) || name.equals(EXPANDABLE_LIST_VIEW)) {
+ mMenuManager.insertBefore(endId, new Separator());
+ mMenuManager.insertBefore(endId, new ListViewTypeMenu(mCanvas));
+ return;
+ }
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ExtractIncludeAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ExtractIncludeAction.java
deleted file mode 100644
index d3ff090..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ExtractIncludeAction.java
+++ /dev/null
@@ -1,837 +0,0 @@
-/*
- * Copyright (C) 2010 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.editors.layout.gle2;
-
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_PREFIX;
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
-import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
-import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
-import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
-import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
-import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
-import static com.android.ide.eclipse.adt.AndroidConstants.DOT_XML;
-import static com.android.ide.eclipse.adt.AndroidConstants.WS_SEP;
-import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS;
-import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS_COLON;
-import static com.android.resources.ResourceType.LAYOUT;
-
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
-import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
-import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.ResourceNameValidator;
-import com.android.util.Pair;
-
-import org.eclipse.core.resources.IContainer;
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.core.runtime.QualifiedName;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.dialogs.IInputValidator;
-import org.eclipse.jface.dialogs.InputDialog;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.window.Window;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.ide.IDE;
-import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
-import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
-import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
-import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
-import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
-import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
-import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Extracts the selection and writes it out as a separate layout file, then adds an
- * include to that new layout file. Interactively asks the user for a new name for the
- * layout.
- */
-@SuppressWarnings("restriction") // For XML model
-public class ExtractIncludeAction extends Action {
- private LayoutCanvas mCanvas;
-
- public ExtractIncludeAction(LayoutCanvas canvas) {
- super("Extract as Include...", IAction.AS_PUSH_BUTTON);
- mCanvas = canvas;
- }
-
- @Override
- public boolean isEnabled() {
- List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
- if (selection.size() == 0) {
- return false;
- }
-
- // Can't extract the root -- wouldn't that be pointless? (or maybe not always)
- for (SelectionItem item : selection) {
- if (item.isRoot()) {
- return false;
- }
- }
-
- // Disable if you've selected a single include tag
- if (selection.size() == 1) {
- UiViewElementNode uiNode = selection.get(0).getViewInfo().getUiViewNode();
- if (uiNode != null) {
- Node xmlNode = uiNode.getXmlNode();
- if (xmlNode.getLocalName().equals(LayoutDescriptors.VIEW_INCLUDE)) {
- return false;
- }
- }
- }
-
- // Enforce that the selection is -contiguous-
- if (selection.size() > 1) {
- // All elements must be siblings (e.g. same parent)
- List<UiViewElementNode> nodes = new ArrayList<UiViewElementNode>(selection.size());
- for (SelectionItem item : selection) {
- UiViewElementNode node = item.getViewInfo().getUiViewNode();
- if (node != null) {
- nodes.add(node);
- }
- }
- if (nodes.size() == 0) {
- return false;
- }
-
- UiElementNode parent = nodes.get(0).getUiParent();
- for (UiViewElementNode node : nodes) {
- if (parent != node.getUiParent()) {
- return false;
- }
- }
- // Ensure that the siblings are contiguous; no gaps.
- // If we've selected all the children of the parent then we don't need to look.
- List<UiElementNode> siblings = parent.getUiChildren();
- if (siblings.size() != nodes.size()) {
- Set<UiViewElementNode> nodeSet = new HashSet<UiViewElementNode>(nodes);
- boolean inRange = false;
- int remaining = nodes.size();
- for (UiElementNode node : siblings) {
- boolean in = nodeSet.contains(node);
- if (in) {
- remaining--;
- if (remaining == 0) {
- break;
- }
- inRange = true;
- } else if (inRange) {
- return false;
- }
- }
- }
- }
-
- return true;
- }
-
- private String inputName() {
- IProject project = mCanvas.getLayoutEditor().getProject();
- IInputValidator validator = ResourceNameValidator.create(true, project, LAYOUT);
-
- String defaultName = ""; //$NON-NLS-1$
- Element primaryNode = getPrimaryNode();
- if (primaryNode != null) {
- String id = primaryNode.getAttributeNS(ANDROID_URI, ATTR_ID);
- if (id.startsWith(ID_PREFIX) || id.startsWith(NEW_ID_PREFIX)) {
- // Use everything following the id/, and make it lowercase since that is
- // the convention for layouts
- defaultName = id.substring(id.indexOf('/') + 1).toLowerCase();
- if (validator.isValid(defaultName) != null) { // Already exists?
- defaultName = ""; //$NON-NLS-1$
- }
- }
- }
-
- InputDialog d = new InputDialog(AdtPlugin.getDisplay().getActiveShell(),
- "Extract As Include", // title
- "New Layout Name", defaultName, validator);
-
- if (d.open() != Window.OK) {
- return null;
- }
-
- return d.getValue().trim();
- }
-
- @Override
- public void run() {
- String newName = inputName();
- if (newName == null) {
- // User canceled
- return;
- }
-
- // Create extracted content
- // In order to ensure that we preserve as much of the user's original formatting
- // and attribute order as possible, we will just snip out the exact element ranges
- // from the current source editor and reindent them in the new file
- Pair<Integer, Integer> range = computeExtractRange();
- if (range == null) {
- return;
- }
- int start = range.getFirst();
- int end = range.getSecond();
- String extractedText = getExtractedText(start, end);
-
- Pair<String, String> namespace = computeNamespaces();
- String androidNsPrefix = namespace.getFirst();
- String namespaceDeclarations = namespace.getSecond();
-
- // Insert namespace:
- extractedText = insertNamespace(extractedText, namespaceDeclarations);
-
- StringBuilder sb = new StringBuilder();
- sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); //$NON-NLS-1$
- sb.append(extractedText);
- sb.append('\n');
-
- String newFileName = newName + DOT_XML;
- IProject project = mCanvas.getLayoutEditor().getProject();
- IContainer parent = mCanvas.getLayoutEditor().getInputFile().getParent();
- IPath parentPath = parent.getProjectRelativePath();
- IFile file = project.getFile(new Path(parentPath + WS_SEP + newFileName));
-
- writeFile(file, sb.toString());
-
- // Force refresh to pick up the newly available @layout/<newName>
- LayoutEditor editor = mCanvas.getLayoutEditor();
- editor.getGraphicalEditor().refreshProjectResources();
-
- String referenceId = getReferenceId();
- List<Edit> edits = new ArrayList<Edit>();
-
- // Replace existing elements in the source file and insert <include>
- String include = computeIncludeString(newName, androidNsPrefix, referenceId);
- edits.add(new Edit(start, end, include));
-
- // Update any layout references to the old id with the new id
- if (referenceId != null) {
- String rootId = getRootId();
- IStructuredModel model = editor.getModelForRead();
- try {
- IStructuredDocument doc = model.getStructuredDocument();
- if (doc != null) {
- List<Edit> replaceIds = replaceIds(doc, start, end, rootId, referenceId);
- edits.addAll(replaceIds);
- }
- } finally {
- model.releaseFromRead();
- }
- }
-
- // Open extracted file. This seems to trigger refreshing of ProjectResources
- // such that the @layout/<newName> reference from the new <include> we're adding
- // will work; without this we get file reference errors
- openFile(file);
-
- // Perform edits
- applyEdits("Extract As Include", edits);
- }
-
- /** Produce a list of edits to replace references to the given id with the given new id */
- private List<Edit> replaceIds(IStructuredDocument doc, int skipStart, int skipEnd,
- String rootId, String referenceId) {
-
- // We need to search for either @+id/ or @id/
- String match1 = rootId;
- String match2;
- if (match1.startsWith(ID_PREFIX)) {
- match2 = '"' + NEW_ID_PREFIX + match1.substring(ID_PREFIX.length()) + '"';
- match1 = '"' + match1 + '"';
- } else if (match1.startsWith(NEW_ID_PREFIX)) {
- match2 = '"' + ID_PREFIX + match1.substring(NEW_ID_PREFIX.length()) + '"';
- match1 = '"' + match1 + '"';
- } else {
- return Collections.emptyList();
- }
-
- String namePrefix = ANDROID_NS_PREFIX + ':' + ATTR_LAYOUT_PREFIX;
- List<Edit> edits = new ArrayList<Edit>();
-
- IStructuredDocumentRegion region = doc.getFirstStructuredDocumentRegion();
- for (; region != null; region = region.getNext()) {
- ITextRegionList list = region.getRegions();
- int regionStart = region.getStart();
-
- // Look at all attribute values and look for an id reference match
- String attributeName = ""; //$NON-NLS-1$
- for (int j = 0; j < region.getNumberOfRegions(); j++) {
- ITextRegion subRegion = list.get(j);
- String type = subRegion.getType();
- if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(type)) {
- attributeName = region.getText(subRegion);
- } else if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(type)) {
- // Only replace references in layout attributes
- if (!attributeName.startsWith(namePrefix)) {
- continue;
- }
- // Skip occurrences in the given skip range
- int subRegionStart = regionStart + subRegion.getStart();
- if (subRegionStart >= skipStart && subRegionStart <= skipEnd) {
- continue;
- }
-
- String attributeValue = region.getText(subRegion);
- if (attributeValue.equals(match1) || attributeValue.equals(match2)) {
- int start = subRegionStart + 1; // skip quote
- int end = start + rootId.length();
- edits.add(new Edit(start, end, referenceId));
- }
- }
- }
- }
-
- return edits;
- }
-
- /** Returns the id to be used for the include tag itself (may be null) */
- private String getReferenceId() {
- String rootId = getRootId();
- if (rootId != null) {
- return rootId + "_ref";
- }
-
- return null;
- }
-
- /** Get the id of the root selected element, if any */
- private String getRootId() {
- Element primaryNode = getPrimaryNode();
- if (primaryNode != null) {
- String oldId = primaryNode.getAttributeNS(ANDROID_URI, ATTR_ID);
- if (oldId.length() > 0) {
- return oldId;
- }
- }
-
- return null;
- }
-
- private boolean writeFile(IFile file, String content) {
- // Write out the content into the new XML file
- try {
- byte[] buf = content.getBytes("UTF8"); //$NON-NLS-1$
- InputStream stream = new ByteArrayInputStream(buf);
- file.create(stream, true /* force */, null /* progress */);
- return true;
- } catch (Exception e) {
- String message = e.getMessage();
- String error = String.format("Failed to generate %1$s: %2$s", file.getName(), message);
- AdtPlugin.displayError("Extract As Include", error);
- return false;
- }
- }
-
- private Pair<String, String> computeNamespaces() {
- String androidNsPrefix = null;
- String namespaceDeclarations = null;
-
- Document document = getDocument();
- if (document != null) {
- StringBuilder sb = new StringBuilder();
- Element root = document.getDocumentElement();
- NamedNodeMap attributes = root.getAttributes();
- for (int i = 0, n = attributes.getLength(); i < n; i++) {
- Node attributeNode = attributes.item(i);
-
- String prefix = attributeNode.getPrefix();
- if (XMLNS.equals(prefix)) {
- sb.append(' ');
- String name = attributeNode.getNodeName();
- sb.append(name);
- sb.append('=').append('"');
-
- String value = attributeNode.getNodeValue();
- if (value.equals(ANDROID_URI)) {
- androidNsPrefix = name;
- if (androidNsPrefix.startsWith(XMLNS_COLON)) {
- androidNsPrefix = androidNsPrefix.substring(XMLNS_COLON.length());
- }
- }
- sb.append(DescriptorsUtils.toXmlAttributeValue(value));
- sb.append('"');
- }
- }
-
- namespaceDeclarations = sb.toString();
- }
-
- if (androidNsPrefix == null) {
- androidNsPrefix = ANDROID_NS_PREFIX;
- }
- if (namespaceDeclarations == null) {
- StringBuilder sb = new StringBuilder();
- sb.append(' ');
- sb.append(XMLNS_COLON);
- sb.append(ANDROID_NS_PREFIX);
- sb.append('=').append('"');
- sb.append(ANDROID_URI);
- sb.append('"');
- namespaceDeclarations = sb.toString();
- }
-
- return Pair.of(androidNsPrefix, namespaceDeclarations);
- }
-
- private String insertNamespace(String xmlText, String namespaceDeclarations) {
- // Insert namespace declarations into the extracted XML fragment
- int firstSpace = xmlText.indexOf(' ');
- int elementEnd = xmlText.indexOf('>');
- int insertAt;
- if (firstSpace != -1 && firstSpace < elementEnd) {
- insertAt = firstSpace;
- } else {
- insertAt = elementEnd;
- }
- xmlText = xmlText.substring(0, insertAt) + namespaceDeclarations
- + xmlText.substring(insertAt);
-
- return xmlText;
- }
-
- private void openFile(IFile file) {
- LayoutEditor editor = mCanvas.getLayoutEditor();
- GraphicalEditorPart graphicalEditor = editor.getGraphicalEditor();
- IFile leavingFile = graphicalEditor.getEditedFile();
-
- try {
- // Duplicate the current state into the newly created file
- QualifiedName qname = ConfigurationComposite.NAME_CONFIG_STATE;
- String state = AdtPlugin.getFileProperty(leavingFile, qname);
- file.setSessionProperty(GraphicalEditorPart.NAME_INITIAL_STATE, state);
- } catch (CoreException e) {
- // pass
- }
-
- /* TBD: "Show Included In" if supported.
- * Not sure if this is a good idea.
- if (graphicalEditor.renderingSupports(Capability.EMBEDDED_LAYOUT)) {
- try {
- Reference include = Reference.create(graphicalEditor.getEditedFile());
- file.setSessionProperty(GraphicalEditorPart.NAME_INCLUDE, include);
- } catch (CoreException e) {
- // pass - worst that can happen is that we don't start with inclusion
- }
- }
- */
-
- try {
- IEditorPart part = IDE.openEditor(editor.getEditorSite().getPage(), file);
- if (part instanceof AndroidXmlEditor && AdtPrefs.getPrefs().getFormatXml()) {
- AndroidXmlEditor newEditor = (AndroidXmlEditor) part;
- newEditor.reformatDocument();
- }
- } catch (PartInitException e) {
- AdtPlugin.log(e, "Can't open new included layout");
- }
- }
-
- /**
- * Compute the actual {@code <include>} string to be inserted in place of the old
- * selection
- */
- private String computeIncludeString(String newName, String androidNsPrefix,
- String referenceId) {
- StringBuilder sb = new StringBuilder();
- sb.append("<include layout=\"@layout/"); //$NON-NLS-1$
- sb.append(newName);
- sb.append('"');
- sb.append(' ');
-
- // Create new id for the include itself
- if (referenceId != null) {
- sb.append(androidNsPrefix);
- sb.append(':');
- sb.append(ATTR_ID);
- sb.append('=').append('"');
- sb.append(referenceId);
- sb.append('"').append(' ');
- }
-
- // Add id string, unless it's a <merge>, since we may need to adjust any layout
- // references to apply to the <include> tag instead
- // TODO: Use refactoring infrastructure to handle this part
-
- // I should move all the layout_ attributes as well
- // I also need to duplicate and modify the id and then replace
- // everything else in the file with this new id...
-
- // HACK: see issue 13494: We must duplicate the width/height attributes on the
- // <include> statement for designtime rendering only
- Element primaryNode = getPrimaryNode();
- String width = null;
- String height = null;
- if (primaryNode == null) {
- // Multiple selection - in that case we will be creating an outer <merge>
- // so we need to set our own width/height on it
- width = height = VALUE_WRAP_CONTENT;
- } else {
- if (!primaryNode.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH)) {
- width = VALUE_WRAP_CONTENT;
- } else {
- width = primaryNode.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH);
- }
- if (!primaryNode.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT)) {
- height = VALUE_WRAP_CONTENT;
- } else {
- height = primaryNode.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT);
- }
- }
- if (width != null) {
- sb.append(' ');
- sb.append(androidNsPrefix);
- sb.append(':');
- sb.append(ATTR_LAYOUT_WIDTH);
- sb.append('=').append('"');
- sb.append(DescriptorsUtils.toXmlAttributeValue(width));
- sb.append('"');
- }
- if (height != null) {
- sb.append(' ');
- sb.append(androidNsPrefix);
- sb.append(':');
- sb.append(ATTR_LAYOUT_HEIGHT);
- sb.append('=').append('"');
- sb.append(DescriptorsUtils.toXmlAttributeValue(height));
- sb.append('"');
- }
-
- // Duplicate all the other layout attributes as well
- if (primaryNode != null) {
- NamedNodeMap attributes = primaryNode.getAttributes();
- for (int i = 0, n = attributes.getLength(); i < n; i++) {
- Node attr = attributes.item(i);
- String name = attr.getLocalName();
- if (name.startsWith(ATTR_LAYOUT_PREFIX)
- && ANDROID_URI.equals(attr.getNamespaceURI())) {
- if (name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT)) {
- // Already handled
- continue;
- }
-
- sb.append(' ');
- sb.append(androidNsPrefix);
- sb.append(':');
- sb.append(name);
- sb.append('=').append('"');
- sb.append(DescriptorsUtils.toXmlAttributeValue(attr.getNodeValue()));
- sb.append('"');
- }
- }
- }
-
- sb.append("/>");
- return sb.toString();
- }
-
- /** Return the text in the document in the range start to end */
- private String getExtractedText(int start, int end) {
- LayoutEditor editor = mCanvas.getLayoutEditor();
-
- IStructuredModel model = editor.getModelForRead();
- try {
- IStructuredDocument document = editor.getStructuredDocument();
- String xml = document.get(start, end - start);
- Element primaryNode = getPrimaryNode();
- xml = stripTopLayoutAttributes(primaryNode, start, xml);
- xml = dedent(xml);
-
- // Wrap siblings in <merge>?
- if (primaryNode == null) {
- StringBuilder sb = new StringBuilder();
- sb.append("<merge>\n"); //$NON-NLS-1$
- // indent an extra level
- for (String line : xml.split("\n")) { //$NON-NLS-1$
- sb.append(" "); //$NON-NLS-1$
- sb.append(line).append('\n');
- }
- sb.append("</merge>\n"); //$NON-NLS-1$
- xml = sb.toString();
- }
-
- return xml;
- } catch (BadLocationException e) {
- // the region offset was invalid. ignore.
- return null;
- } finally {
- model.releaseFromRead();
- }
- }
-
- /** Remove sections of the document that correspond to top level layout attributes;
- * these are placed on the include element instead */
- private String stripTopLayoutAttributes(Element primaryNode, int start, String xml) {
- if (primaryNode != null) {
- // List of attributes to remove
- //IndexedRegion attRegion = (IndexedRegion) attrs.item(i);
- List<IndexedRegion> skip = new ArrayList<IndexedRegion>();
- NamedNodeMap attributes = primaryNode.getAttributes();
- for (int i = 0, n = attributes.getLength(); i < n; i++) {
- Node attr = attributes.item(i);
- String name = attr.getLocalName();
- if (name.startsWith(ATTR_LAYOUT_PREFIX)
- && ANDROID_URI.equals(attr.getNamespaceURI())) {
- if (name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT)) {
- // These are special and are left in
- continue;
- }
-
- if (attr instanceof IndexedRegion) {
- skip.add((IndexedRegion) attr);
- }
- }
- }
- if (skip.size() > 0) {
- Collections.sort(skip, new Comparator<IndexedRegion>() {
- // Sort in start order
- public int compare(IndexedRegion r1, IndexedRegion r2) {
- return r1.getStartOffset() - r2.getStartOffset();
- }
- });
-
- // Successively cut out the various layout attributes
- // TODO remove adjacent whitespace too (but not newlines, unless they
- // are newly adjacent)
- StringBuilder sb = new StringBuilder(xml.length());
- int nextStart = 0;
-
- // Copy out all the sections except the skip sections
- for (IndexedRegion r : skip) {
- int regionStart = r.getStartOffset();
- // Adjust to string offsets since we've copied the string out of
- // the document
- regionStart -= start;
-
- sb.append(xml.substring(nextStart, regionStart));
-
- nextStart = regionStart + r.getLength();
- }
- if (nextStart < xml.length()) {
- sb.append(xml.substring(nextStart));
- }
-
- return sb.toString();
- }
- }
-
- return xml;
- }
-
- private static String getIndent(String line, int max) {
- int i = 0;
- int n = Math.min(max, line.length());
- for (; i < n; i++) {
- char c = line.charAt(i);
- if (!Character.isWhitespace(c)) {
- return line.substring(0, i);
- }
- }
-
- if (n < line.length()) {
- return line.substring(0, n);
- } else {
- return line;
- }
- }
-
- private static String dedent(String xml) {
- String[] lines = xml.split("\n"); //$NON-NLS-1$
- if (lines.length < 2) {
- // The first line never has any indentation since we copy it out from the
- // element start index
- return xml;
- }
-
- String indentPrefix = getIndent(lines[1], lines[1].length());
- for (int i = 2, n = lines.length; i < n; i++) {
- String line = lines[i];
-
- // Ignore blank lines
- if (line.trim().length() == 0) {
- continue;
- }
-
- indentPrefix = getIndent(line, indentPrefix.length());
-
- if (indentPrefix.length() == 0) {
- return xml;
- }
- }
-
- StringBuilder sb = new StringBuilder();
- for (String line : lines) {
- if (line.startsWith(indentPrefix)) {
- sb.append(line.substring(indentPrefix.length()));
- } else {
- sb.append(line);
- }
- sb.append('\n');
- }
- return sb.toString();
- }
-
- private Element getPrimaryNode() {
- List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
- if (selection.size() == 1) {
- UiViewElementNode node = selection.get(0).getViewInfo().getUiViewNode();
- if (node != null) {
- Node xmlNode = node.getXmlNode();
- if (xmlNode instanceof Element) {
- return (Element) xmlNode;
- }
- }
- }
-
- return null;
- }
-
- private Document getDocument() {
- List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
- for (SelectionItem item : selection) {
- UiViewElementNode node = item.getViewInfo().getUiViewNode();
- if (node != null) {
- Node xmlNode = node.getXmlNode();
- if (xmlNode != null) {
- return xmlNode.getOwnerDocument();
- }
- }
- }
-
- return null;
- }
-
- private Pair<Integer, Integer> computeExtractRange() {
- List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
- if (selection.size() == 0) {
- return null;
- }
- int end = Integer.MIN_VALUE;
- int start = Integer.MAX_VALUE;
- for (SelectionItem item : selection) {
- CanvasViewInfo viewInfo = item.getViewInfo();
- UiViewElementNode uiNode = viewInfo.getUiViewNode();
- if (uiNode == null) {
- continue;
- }
- Node xmlNode = uiNode.getXmlNode();
- if (xmlNode instanceof IndexedRegion) {
- IndexedRegion region = (IndexedRegion) xmlNode;
-
- start = Math.min(start, region.getStartOffset());
- end = Math.max(end, region.getEndOffset());
- }
- }
- if (start < 0) {
- return null;
- }
-
- return Pair.of(start, end);
- }
-
- /** Apply edits into document under an undo lock */
- private void applyEdits(String label, final List<Edit> edits) {
- // Process the edits in reverse document position order to ensure
- // that the offsets aren't affected by other edits
- Collections.sort(edits);
- final LayoutEditor editor = mCanvas.getLayoutEditor();
- editor.wrapUndoEditXmlModel(label, new Runnable() {
- public void run() {
- IStructuredDocument document = editor.getStructuredDocument();
- if (document != null) {
- try {
- for (Edit edit : edits) {
- edit.apply(document);
- }
- } catch (BadLocationException e) {
- AdtPlugin.log(e, "Cannot insert <include> tag");
- return;
- }
- }
- }
- });
-
- // Save file to trigger include finder scanning (as well as making the
- // actual show-include feature work since it relies on reading files from
- // disk, not a live buffer)
- IEditorPart editorPart = editor;
- IWorkbenchPage page = editorPart.getEditorSite().getPage();
- page.saveEditor(editorPart, false);
- }
-
- /**
- * Edit operation (insert, delete, replace) at a given offset - a collection of these
- * can be sorted in reverse offset order such that they can be applied successively
- * without having to updated offsets
- * <p>
- * TODO: When rewriting this extract operation to the refactoring framework, use
- * refactoring framework's change list instead
- */
- private static class Edit implements Comparable<Edit> {
- private final int mStart;
- private final int mEnd;
- private final String mReplaceWith;
-
- public Edit(int start, int end, String replaceWith) {
- super();
- mStart = start;
- mEnd = end;
- mReplaceWith = replaceWith;
- }
-
- void apply(IStructuredDocument document) throws BadLocationException {
- document.replace(mStart, mEnd - mStart, mReplaceWith);
- }
-
- public int compareTo(Edit o) {
- // Sort in *reverse* offset order
- return o.mStart - mStart;
- }
-
- @Override
- public String toString() {
- return "Edit [start=" + mStart + ", end=" + mEnd + ", replaceWith=" + mReplaceWith
- + "]";
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
index 5506319..337ad5c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
@@ -17,9 +17,10 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_STRING_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.LAYOUT_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.SCROLL_VIEW;
import static com.android.ide.common.layout.LayoutConstants.STRING_PREFIX;
-import static com.android.ide.eclipse.adt.AndroidConstants.ANDROID_PKG;
+import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
import com.android.ide.common.rendering.LayoutLibrary;
import com.android.ide.common.rendering.StaticRenderSession;
@@ -32,10 +33,14 @@ import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
import com.android.ide.common.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.editors.IPageImageProvider;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.ContextPullParser;
@@ -51,27 +56,24 @@ import com.android.ide.eclipse.adt.internal.editors.layout.configuration.LayoutC
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.IConfigListener;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
import com.android.ide.eclipse.adt.internal.editors.ui.DecorComposite;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
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.Sdk;
import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
import com.android.ide.eclipse.adt.io.IFileWrapper;
import com.android.ide.eclipse.adt.io.IFolderWrapper;
+import com.android.io.IAbstractFile;
+import com.android.io.StreamException;
import com.android.resources.Density;
+import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.StreamException;
import com.android.sdklib.xml.AndroidManifest;
import org.eclipse.core.resources.IFile;
@@ -236,6 +238,7 @@ public class GraphicalEditorPart extends EditorPart
private TargetListener mTargetListener;
private ConfigListener mConfigListener;
+ private ResourceResolver mResourceResolver;
private ReloadListener mReloadListener;
@@ -245,6 +248,14 @@ public class GraphicalEditorPart extends EditorPart
private int mTargetSdkVersion;
private LayoutActionBar mActionBar;
+ /**
+ * Flags which tracks whether this editor is currently active which is set whenever
+ * {@link #activated()} is called and clear whenever {@link #deactivated()} is called.
+ * This is used to suppress repeated calls to {@link #activate()} to avoid doing
+ * unnecessary work.
+ */
+ private boolean mActive;
+
public GraphicalEditorPart(LayoutEditor layoutEditor) {
mLayoutEditor = layoutEditor;
setPartName("Graphical Layout");
@@ -322,9 +333,9 @@ public class GraphicalEditorPart extends EditorPart
mSashPalette = new SashForm(parent, SWT.HORIZONTAL);
mSashPalette.setLayoutData(new GridData(GridData.FILL_BOTH));
- DecorComposite paleteDecor = new DecorComposite(mSashPalette, SWT.BORDER);
- paleteDecor.setContent(new PaletteControl.PaletteDecor(this));
- mPalette = (PaletteControl) paleteDecor.getContentControl();
+ DecorComposite paletteDecor = new DecorComposite(mSashPalette, SWT.BORDER);
+ paletteDecor.setContent(new PaletteControl.PaletteDecor(this));
+ mPalette = (PaletteControl) paletteDecor.getContentControl();
Composite layoutBarAndCanvas = new Composite(mSashPalette, SWT.NONE);
GridLayout gridLayout = new GridLayout(1, false);
@@ -428,6 +439,7 @@ public class GraphicalEditorPart extends EditorPart
*/
public void onConfigurationChange() {
mConfiguredFrameworkRes = mConfiguredProjectRes = null;
+ mResourceResolver = null;
if (mEditedFile == null || mConfigComposite.getEditedConfig() == null) {
return;
@@ -503,6 +515,7 @@ public class GraphicalEditorPart extends EditorPart
public void onThemeChange() {
// Store the state in the current file
mConfigComposite.storeState();
+ mResourceResolver = null;
recomputeLayout();
@@ -533,7 +546,7 @@ public class GraphicalEditorPart extends EditorPart
public Map<ResourceType, Map<String, ResourceValue>> getConfiguredFrameworkResources() {
if (mConfiguredFrameworkRes == null && mConfigComposite != null) {
- ProjectResources frameworkRes = getFrameworkResources();
+ ResourceRepository frameworkRes = getFrameworkResources();
if (frameworkRes == null) {
AdtPlugin.log(IStatus.ERROR, "Failed to get ProjectResource for the framework");
@@ -551,9 +564,6 @@ public class GraphicalEditorPart extends EditorPart
if (mConfiguredProjectRes == null && mConfigComposite != null) {
ProjectResources project = getProjectResources();
- // make sure they are loaded
- project.loadAll();
-
// get the project resource values based on the current config
mConfiguredProjectRes = project.getConfiguredResources(
mConfigComposite.getCurrentConfig());
@@ -567,7 +577,7 @@ public class GraphicalEditorPart extends EditorPart
* configuration selection.
* @return the framework resources or null if not found.
*/
- public ProjectResources getFrameworkResources() {
+ public ResourceRepository getFrameworkResources() {
return getFrameworkResources(getRenderingTarget());
}
@@ -577,7 +587,7 @@ public class GraphicalEditorPart extends EditorPart
* @param target the target for which to return the framework resources.
* @return the framework resources or null if not found.
*/
- public ProjectResources getFrameworkResources(IAndroidTarget target) {
+ public ResourceRepository getFrameworkResources(IAndroidTarget target) {
if (target != null) {
AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
@@ -589,7 +599,6 @@ public class GraphicalEditorPart extends EditorPart
return null;
}
-
public ProjectResources getProjectResources() {
if (mEditedFile != null) {
ResourceManager manager = ResourceManager.getInstance();
@@ -716,6 +725,21 @@ public class GraphicalEditorPart extends EditorPart
}
}.schedule();
}
+
+ /**
+ * When the device changes, zoom the view to fit, but only up to 100% (e.g. zoom
+ * out to fit the content, or zoom back in if we were zoomed out more from the
+ * previous view, but only up to 100% such that we never blow up pixels
+ */
+ public void onDevicePostChange() {
+ if (mActionBar.isZoomingAllowed()) {
+ getCanvasControl().setFitScale(true);
+ }
+ }
+
+ public String getIncludedWithin() {
+ return mIncludedWithin != null ? mIncludedWithin.getName() : null;
+ }
}
/**
@@ -759,6 +783,7 @@ public class GraphicalEditorPart extends EditorPart
// because the target changed we must reset the configured resources.
mConfiguredFrameworkRes = mConfiguredProjectRes = null;
+ mResourceResolver = null;
// make sure we remove the custom view loader, since its parent class loader is the
// bridge class loader.
@@ -775,9 +800,9 @@ public class GraphicalEditorPart extends EditorPart
}
/** Refresh the configured project resources associated with this editor */
- /*package*/ void refreshProjectResources() {
+ public void refreshProjectResources() {
mConfiguredProjectRes = null;
- mConfigListener.getConfiguredProjectResources();
+ mResourceResolver = null;
}
/**
@@ -866,8 +891,18 @@ public class GraphicalEditorPart extends EditorPart
* Responds to a page change that made the Graphical editor page the activated page.
*/
public void activated() {
- if (mNeedsRecompute) {
- recomputeLayout();
+ if (!mActive) {
+ mActive = true;
+
+ boolean changed = mConfigComposite.syncRenderState();
+ if (changed) {
+ // Will also force recomputeLayout()
+ return;
+ }
+
+ if (mNeedsRecompute) {
+ recomputeLayout();
+ }
}
}
@@ -875,7 +910,7 @@ public class GraphicalEditorPart extends EditorPart
* Responds to a page change that made the Graphical editor page the deactivated page
*/
public void deactivated() {
- // nothing to be done here for now.
+ mActive = false;
}
/**
@@ -1027,7 +1062,7 @@ public class GraphicalEditorPart extends EditorPart
new StaticRenderSession(
Result.Status.SUCCESS.createResult(),
null /*rootViewInfo*/, null /*image*/),
- null /*explodeNodes*/);
+ null /*explodeNodes*/, true /* layoutlib5 */);
return;
}
@@ -1313,7 +1348,8 @@ public class GraphicalEditorPart extends EditorPart
explodeNodes, null /*custom background*/, false /*no decorations*/, logger,
mIncludedWithin, renderingMode);
- canvas.setSession(session, explodeNodes);
+ boolean layoutlib5 = layoutLib.supports(Capability.EMBEDDED_LAYOUT);
+ canvas.setSession(session, explodeNodes, layoutlib5);
// update the UiElementNode with the layout info.
if (session != null && session.getResult().isSuccess() == false) {
@@ -1351,60 +1387,33 @@ public class GraphicalEditorPart extends EditorPart
model.refreshUi();
}
- private RenderSession renderWithBridge(IProject iProject, UiDocumentNode model,
+ private RenderSession renderWithBridge(IProject project, UiDocumentNode model,
LayoutLibrary layoutLib, int width, int height, Set<UiElementNode> explodeNodes,
Integer overrideBgColor, boolean noDecor, LayoutLog logger, Reference includeWithin,
RenderingMode renderingMode) {
ResourceManager resManager = ResourceManager.getInstance();
- ProjectResources projectRes = resManager.getProjectResources(iProject);
+ ProjectResources projectRes = resManager.getProjectResources(project);
if (projectRes == null) {
displayError("Missing project resources.");
return null;
}
- // Get the resources of the file's project.
- Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes =
- mConfigListener.getConfiguredProjectResources();
-
- // Get the framework resources
- Map<ResourceType, Map<String, ResourceValue>> frameworkResources =
- mConfigListener.getConfiguredFrameworkResources();
-
- // Abort the rendering if the resources are not found.
- if (configuredProjectRes == null) {
- displayError("Missing project resources for current configuration.");
- return null;
- }
-
- if (frameworkResources == null) {
- displayError("Missing framework resources.");
- return null;
- }
-
// Lazily create the project callback the first time we need it
if (mProjectCallback == null) {
- mProjectCallback = new ProjectCallback(
- layoutLib.getClassLoader(), projectRes, iProject);
+ mProjectCallback = new ProjectCallback(layoutLib, projectRes, project);
} else {
// Also clears the set of missing/broken classes prior to rendering
mProjectCallback.getMissingClasses().clear();
mProjectCallback.getUninstantiatableClasses().clear();
}
- // get the selected theme
- String theme = mConfigComposite.getTheme();
- if (theme == null) {
- displayError("Missing theme.");
- return null;
- }
-
if (mUseExplodeMode) {
// compute how many padding in x and y will bump the screen size
List<UiElementNode> children = model.getUiChildren();
if (children.size() == 1) {
ExplodedRenderingHelper helper = new ExplodedRenderingHelper(
- children.get(0).getXmlNode(), iProject);
+ children.get(0).getXmlNode(), project);
// there are 2 paddings for each view
// left and right, or top and bottom.
@@ -1418,22 +1427,22 @@ public class GraphicalEditorPart extends EditorPart
Density density = mConfigComposite.getDensity();
float xdpi = mConfigComposite.getXDpi();
float ydpi = mConfigComposite.getYDpi();
- boolean isProjectTheme = mConfigComposite.isProjectTheme();
ILayoutPullParser modelParser = new UiElementPullParser(model,
- mUseExplodeMode, explodeNodes, density, xdpi, iProject);
+ mUseExplodeMode, explodeNodes, density, xdpi, project);
ILayoutPullParser topParser = modelParser;
// Code to support editing included layout
+ // first reset the layout parser just in case.
+ mProjectCallback.setLayoutParser(null, null);
- // Outer layout name:
if (includeWithin != null) {
+ // Outer layout name:
String contextLayoutName = includeWithin.getName();
// Find the layout file.
- Map<String, ResourceValue> layouts = configuredProjectRes.get(
- ResourceType.LAYOUT);
- ResourceValue contextLayout = layouts.get(contextLayoutName);
+ ResourceValue contextLayout = getResourceResolver().findResValue(
+ LAYOUT_PREFIX + contextLayoutName , false /* forceFrameworkOnly*/);
if (contextLayout != null) {
File layoutFile = new File(contextLayout.getValue());
if (layoutFile.isFile()) {
@@ -1441,7 +1450,8 @@ public class GraphicalEditorPart extends EditorPart
// Get the name of the layout actually being edited, without the extension
// as it's what IXmlPullParser.getParser(String) will receive.
String queryLayoutName = getLayoutResourceName();
- topParser = new ContextPullParser(queryLayoutName, modelParser);
+ mProjectCallback.setLayoutParser(queryLayoutName, modelParser);
+ topParser = new ContextPullParser(mProjectCallback);
topParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
topParser.setInput(new FileReader(layoutFile));
} catch (XmlPullParserException e) {
@@ -1453,15 +1463,16 @@ public class GraphicalEditorPart extends EditorPart
}
}
- // FIXME: make resource resolver persistent, and only update it when something changes.
- ResourceResolver resolver = ResourceResolver.create(
- configuredProjectRes, frameworkResources,
- theme, isProjectTheme);
+ ResourceResolver resolver = getResourceResolver();
+ if (resolver == null) {
+ // Abort the rendering if the resources are not found.
+ return null;
+ }
SessionParams params = new SessionParams(
topParser,
renderingMode,
- iProject /* projectKey */,
+ project /* projectKey */,
width, height,
density, xdpi, ydpi,
resolver,
@@ -1471,19 +1482,11 @@ public class GraphicalEditorPart extends EditorPart
logger);
if (noDecor) {
params.setForceNoDecor();
- }
-
- // FIXME make persistent and only reload when the manifest (or at least resources) chanage.
- IFolderWrapper projectFolder = new IFolderWrapper(getProject());
- IAbstractFile manifest = AndroidManifest.getManifest(projectFolder);
- if (manifest != null) {
- try {
- params.setAppIcon(AndroidManifest.getApplicationIcon(manifest));
- } catch (Exception e) {
- // ignore.
- }
+ } else {
+ ManifestInfo manifestInfo = ManifestInfo.get(project);
try {
- params.setAppLabel(AndroidManifest.getApplicationLabel(manifest));
+ params.setAppLabel(manifestInfo.getApplicationLabel());
+ params.setAppIcon(manifestInfo.getApplicationIcon());
} catch (Exception e) {
// ignore.
}
@@ -1523,7 +1526,7 @@ public class GraphicalEditorPart extends EditorPart
* @return the image, or null if something went wrong
*/
BufferedImage renderThemeItem(String itemName, int width, int height) {
- ResourceResolver resources = createResolver();
+ ResourceResolver resources = getResourceResolver();
LayoutLibrary layoutLibrary = getLayoutLibrary();
IProject project = getProject();
ResourceValue drawableResourceValue = resources.findItemInTheme(itemName);
@@ -1532,8 +1535,7 @@ public class GraphicalEditorPart extends EditorPart
float ydpi = mConfigComposite.getYDpi();
ResourceManager resManager = ResourceManager.getInstance();
ProjectResources projectRes = resManager.getProjectResources(project);
- ProjectCallback projectCallback = new ProjectCallback(
- layoutLibrary.getClassLoader(), projectRes, project);
+ ProjectCallback projectCallback = new ProjectCallback(layoutLibrary, projectRes, project);
LayoutLog silentLogger = new LayoutLog();
DrawableParams params = new DrawableParams(drawableResourceValue, project,
width, height,
@@ -1551,19 +1553,44 @@ public class GraphicalEditorPart extends EditorPart
return null;
}
- ResourceResolver createResolver() {
- String theme = mConfigComposite.getTheme();
- boolean isProjectTheme = mConfigComposite.isProjectTheme();
- Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes =
- mConfigListener.getConfiguredProjectResources();
+ /**
+ * Returns the {@link ResourceResolver} for this editor
+ *
+ * @return the resolver used to resolve resources for the current configuration of
+ * this editor, or null
+ */
+ public ResourceResolver getResourceResolver() {
+ if (mResourceResolver == null) {
+ String theme = mConfigComposite.getTheme();
+ if (theme == null) {
+ displayError("Missing theme.");
+ return null;
+ }
+ boolean isProjectTheme = mConfigComposite.isProjectTheme();
+
+ Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes =
+ mConfigListener.getConfiguredProjectResources();
- // Get the framework resources
- Map<ResourceType, Map<String, ResourceValue>> frameworkResources =
- mConfigListener.getConfiguredFrameworkResources();
+ // Get the framework resources
+ Map<ResourceType, Map<String, ResourceValue>> frameworkResources =
+ mConfigListener.getConfiguredFrameworkResources();
+
+ if (configuredProjectRes == null) {
+ displayError("Missing project resources for current configuration.");
+ return null;
+ }
+
+ if (frameworkResources == null) {
+ displayError("Missing framework resources.");
+ return null;
+ }
+
+ mResourceResolver = ResourceResolver.create(
+ configuredProjectRes, frameworkResources,
+ theme, isProjectTheme);
+ }
- return ResourceResolver.create(
- configuredProjectRes, frameworkResources,
- theme, isProjectTheme);
+ return mResourceResolver;
}
/**
@@ -1630,7 +1657,8 @@ public class GraphicalEditorPart extends EditorPart
assert mConfigComposite.getDisplay().getThread() == Thread.currentThread();
boolean recompute = false;
- if (flags.rClass) {
+ // we only care about the r class of the main project.
+ if (flags.rClass && libraryChanged == false) {
recompute = true;
if (mEditedFile != null) {
ResourceManager manager = ResourceManager.getInstance();
@@ -1653,8 +1681,7 @@ public class GraphicalEditorPart extends EditorPart
}
// if a resources was modified.
- // also, if a layout in a library was modified.
- if (flags.resources || (libraryChanged && flags.layout)) {
+ if (flags.resources) {
recompute = true;
// TODO: differentiate between single and multi resource file changed, and whether
@@ -1662,6 +1689,7 @@ public class GraphicalEditorPart extends EditorPart
// force a reparse in case a value XML file changed.
mConfiguredProjectRes = null;
+ mResourceResolver = null;
// clear the cache in the bridge in case a bitmap/9-patch changed.
LayoutLibrary layoutLib = getReadyLayoutLib(true /*displayError*/);
@@ -1792,7 +1820,7 @@ public class GraphicalEditorPart extends EditorPart
if (severity == IMarker.SEVERITY_ERROR) {
hasJavaErrors = true;
}
- } else if (markerType.equals(AndroidConstants.MARKER_AAPT_COMPILE)) {
+ } else if (markerType.equals(AdtConstants.MARKER_AAPT_COMPILE)) {
int severity = marker.getAttribute(IMarker.SEVERITY, -1);
if (severity == IMarker.SEVERITY_ERROR) {
hasAaptErrors = true;
@@ -2027,7 +2055,7 @@ public class GraphicalEditorPart extends EditorPart
if (r instanceof ClassLinkStyleRange) {
String fqcn = mErrorLabel.getText(r.start, r.start + r.length - 1);
- if (!Hyperlinks.openJavaClass(getProject(), fqcn)) {
+ if (!AdtPlugin.openJavaClass(getProject(), fqcn)) {
createNewClass(fqcn);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/HoverOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/HoverOverlay.java
index 27a6024..2f46921 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/HoverOverlay.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/HoverOverlay.java
@@ -16,22 +16,35 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
+import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtDrawingStyle.HOVER;
+import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtDrawingStyle.HOVER_SELECTION;
+
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
+import java.util.List;
+
/**
* The {@link HoverOverlay} paints an optional hover on top of the layout,
* highlighting the currently hovered view.
*/
public class HoverOverlay extends Overlay {
+ private final LayoutCanvas mCanvas;
+
/** Hover border color. Must be disposed, it's NOT a system color. */
private Color mHoverStrokeColor;
/** Hover fill color. Must be disposed, it's NOT a system color. */
private Color mHoverFillColor;
+ /** Hover border select color. Must be disposed, it's NOT a system color. */
+ private Color mHoverSelectStrokeColor;
+
+ /** Hover fill select color. Must be disposed, it's NOT a system color. */
+ private Color mHoverSelectFillColor;
+
/** Vertical scaling & scrollbar information. */
private CanvasTransform mVScale;
@@ -48,13 +61,14 @@ public class HoverOverlay extends Overlay {
/**
* Constructs a new {@link HoverOverlay} linked to the given view hierarchy.
*
+ * @param canvas the associated canvas
* @param hScale The {@link CanvasTransform} to use to transfer horizontal layout
* coordinates to screen coordinates.
* @param vScale The {@link CanvasTransform} to use to transfer vertical layout
* coordinates to screen coordinates.
*/
- public HoverOverlay(CanvasTransform hScale, CanvasTransform vScale) {
- super();
+ public HoverOverlay(LayoutCanvas canvas, CanvasTransform hScale, CanvasTransform vScale) {
+ mCanvas = canvas;
this.mHScale = hScale;
this.mVScale = vScale;
}
@@ -67,6 +81,15 @@ public class HoverOverlay extends Overlay {
if (SwtDrawingStyle.HOVER.getFillColor() != null) {
mHoverFillColor = new Color(device, SwtDrawingStyle.HOVER.getFillColor());
}
+
+ if (SwtDrawingStyle.HOVER_SELECTION.getStrokeColor() != null) {
+ mHoverSelectStrokeColor = new Color(device,
+ SwtDrawingStyle.HOVER_SELECTION.getStrokeColor());
+ }
+ if (SwtDrawingStyle.HOVER_SELECTION.getFillColor() != null) {
+ mHoverSelectFillColor = new Color(device,
+ SwtDrawingStyle.HOVER_SELECTION.getFillColor());
+ }
}
@Override
@@ -80,6 +103,16 @@ public class HoverOverlay extends Overlay {
mHoverFillColor.dispose();
mHoverFillColor = null;
}
+
+ if (mHoverSelectStrokeColor != null) {
+ mHoverSelectStrokeColor.dispose();
+ mHoverSelectStrokeColor = null;
+ }
+
+ if (mHoverSelectFillColor != null) {
+ mHoverSelectFillColor.dispose();
+ mHoverSelectFillColor = null;
+ }
}
/**
@@ -117,19 +150,35 @@ public class HoverOverlay extends Overlay {
int w = mHScale.scale(mHoverRect.width);
int h = mVScale.scale(mHoverRect.height);
- if (mHoverStrokeColor != null) {
+
+ boolean hoverIsSelected = false;
+ List<SelectionItem> selections = mCanvas.getSelectionManager().getSelections();
+ for (SelectionItem item : selections) {
+ if (mHoverRect.equals(item.getViewInfo().getSelectionRect())) {
+ hoverIsSelected = true;
+ break;
+ }
+ }
+
+ Color stroke = hoverIsSelected ? mHoverSelectStrokeColor : mHoverStrokeColor;
+ Color fill = hoverIsSelected ? mHoverSelectFillColor : mHoverFillColor;
+
+ if (stroke != null) {
int oldAlpha = gc.getAlpha();
- gc.setForeground(mHoverStrokeColor);
- gc.setLineStyle(SwtDrawingStyle.HOVER.getLineStyle());
- gc.setAlpha(SwtDrawingStyle.HOVER.getStrokeAlpha());
+ gc.setForeground(stroke);
+ gc.setLineStyle(hoverIsSelected ?
+ HOVER_SELECTION.getLineStyle() : HOVER.getLineStyle());
+ gc.setAlpha(hoverIsSelected ?
+ HOVER_SELECTION.getStrokeAlpha() : HOVER.getStrokeAlpha());
gc.drawRectangle(x, y, w, h);
gc.setAlpha(oldAlpha);
}
- if (mHoverFillColor != null) {
+ if (fill != null) {
int oldAlpha = gc.getAlpha();
- gc.setAlpha(SwtDrawingStyle.HOVER.getFillAlpha());
- gc.setBackground(mHoverFillColor);
+ gc.setAlpha(hoverIsSelected ?
+ HOVER_SELECTION.getFillAlpha() : HOVER.getFillAlpha());
+ gc.setBackground(fill);
gc.fillRectangle(x, y, w, h);
gc.setAlpha(oldAlpha);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageControl.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageControl.java
index d3349e4..7af89f8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageControl.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageControl.java
@@ -49,14 +49,16 @@ public class ImageControl extends Canvas implements MouseTrackListener {
private float mScale = 1.0f;
/**
- * Creates an ImageControl rendering the given image, which will be dispose when this
- * control is disposed
+ * Creates an ImageControl rendering the given image, which will be disposed when this
+ * control is disposed (unless the {@link #setDisposeImage} method is called to turn
+ * off auto dispose).
*
* @param parent the parent to add the image control to
* @param style the SWT style to use
* @param image the image to be rendered, which must not be null and should be unique
* for this image control since it will be disposed by this control when
- * the control is disposed
+ * the control is disposed (unless the {@link #setDisposeImage} method is
+ * called to turn off auto dispose)
*/
public ImageControl(Composite parent, int style, Image image) {
super(parent, style | SWT.NO_FOCUS | SWT.DOUBLE_BUFFERED);
@@ -147,14 +149,21 @@ public class ImageControl extends Canvas implements MouseTrackListener {
int destWidth = imageWidth;
int destHeight = imageHeight;
+ int oldGcAlias = gc.getAntialias();
+ int oldGcInterpolation = gc.getInterpolation();
if (mScale != 1.0f) {
destWidth = (int) (mScale * destWidth);
destHeight = (int) (mScale * destHeight);
+ gc.setAntialias(SWT.ON);
+ gc.setInterpolation(SWT.HIGH);
}
gc.drawImage(mImage, 0, 0, imageWidth, imageHeight, rect.x + mLeftMargin, rect.y
+ mTopMargin, destWidth, destHeight);
+ gc.setAntialias(oldGcAlias);
+ gc.setInterpolation(oldGcInterpolation);
+
if (mHoverColor != null && mMouseIn) {
gc.setAlpha(60);
gc.setBackground(mHoverColor);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java
index fedca9c..7832197 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java
@@ -16,29 +16,30 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
-import static com.android.ide.eclipse.adt.AndroidConstants.EXT_XML;
-import static com.android.ide.eclipse.adt.AndroidConstants.WS_LAYOUTS;
-import static com.android.ide.eclipse.adt.AndroidConstants.WS_SEP;
-import static com.android.sdklib.SdkConstants.FD_LAYOUT;
+import static com.android.AndroidConstants.FD_RES_LAYOUT;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.WS_LAYOUTS;
+import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
+import static com.android.resources.ResourceType.LAYOUT;
import static org.eclipse.core.resources.IResourceDelta.ADDED;
import static org.eclipse.core.resources.IResourceDelta.CHANGED;
import static org.eclipse.core.resources.IResourceDelta.CONTENT;
import static org.eclipse.core.resources.IResourceDelta.REMOVED;
import com.android.annotations.VisibleForTesting;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceFolder;
+import com.android.ide.common.resources.ResourceItem;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResourceItem;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager.IResourceListener;
import com.android.ide.eclipse.adt.io.IFileWrapper;
+import com.android.io.IAbstractFile;
import com.android.resources.ResourceType;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.IAbstractFile;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
@@ -61,9 +62,11 @@ import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -87,7 +90,7 @@ public class IncludeFinder {
* {@link IncludeFinder} for this project
*/
private final static QualifiedName INCLUDE_FINDER = new QualifiedName(AdtPlugin.PLUGIN_ID,
- "finder"); //$NON-NLS-1$
+ "includefinder"); //$NON-NLS-1$
/** Project that the include finder locates includes for */
private final IProject mProject;
@@ -183,7 +186,27 @@ public class IncludeFinder {
}
}
- /** For test suite only -- do not call */
+ /**
+ * Returns true if the given resource is included from some other layout in the
+ * project
+ *
+ * @param included the resource to check
+ * @return true if the file is included by some other layout
+ */
+ public boolean isIncluded(IResource included) {
+ ensureInitialized();
+ String mapKey = getMapKey(included);
+ List<String> result = mIncludedBy.get(mapKey);
+ if (result == null) {
+ String name = getResourceName(included);
+ if (!name.equals(mapKey)) {
+ result = mIncludedBy.get(name);
+ }
+ }
+
+ return result != null && result.size() > 0;
+ }
+
@VisibleForTesting
/* package */ List<String> getIncludedBy(String included) {
ensureInitialized();
@@ -395,8 +418,8 @@ public class IncludeFinder {
private void scanProject() {
ProjectResources resources = ResourceManager.getInstance().getProjectResources(mProject);
if (resources != null) {
- ProjectResourceItem[] layouts = resources.getResources(ResourceType.LAYOUT);
- for (ProjectResourceItem layout : layouts) {
+ Collection<ResourceItem> layouts = resources.getResourceItemsOfType(LAYOUT);
+ for (ResourceItem layout : layouts) {
List<ResourceFile> sources = layout.getSourceFileList();
for (ResourceFile source : sources) {
updateFileIncludes(source, false);
@@ -418,7 +441,7 @@ public class IncludeFinder {
* @return true if we updated the includes for the resource file
*/
private boolean updateFileIncludes(ResourceFile resourceFile, boolean singleUpdate) {
- ResourceType[] resourceTypes = resourceFile.getResourceTypes();
+ Collection<ResourceType> resourceTypes = resourceFile.getResourceTypes();
for (ResourceType type : resourceTypes) {
if (type == ResourceType.LAYOUT) {
ensureInitialized();
@@ -665,7 +688,7 @@ public class IncludeFinder {
// /res/layout/foo.xml => "foo"
// /res/layout-land/foo.xml => "-land/foo"
- if (FD_LAYOUT.equals(folderName)) {
+ if (FD_RES_LAYOUT.equals(folderName)) {
// Normal case -- keep just the basename
return name;
} else {
@@ -912,7 +935,7 @@ public class IncludeFinder {
public IFile getFile() {
String reference = mId;
if (!reference.contains(WS_SEP)) {
- reference = SdkConstants.FD_LAYOUT + WS_SEP + reference;
+ reference = FD_RES_LAYOUT + WS_SEP + reference;
}
String projectPath = SdkConstants.FD_RESOURCES + WS_SEP + reference + '.' + EXT_XML;
@@ -998,5 +1021,71 @@ public class IncludeFinder {
public static Reference create(IFile file) {
return new Reference(file.getProject(), getMapKey(file));
}
+
+ /**
+ * Returns the resource name of this layout, such as {@code @layout/foo}.
+ *
+ * @return the resource name
+ */
+ public String getResourceName() {
+ return '@' + FD_RES_LAYOUT + '/' + getName();
+ }
+ }
+
+ /**
+ * Returns a collection of layouts (expressed as resource names, such as
+ * {@code @layout/foo} which would be invalid includes in the given layout
+ * (because it would introduce a cycle)
+ *
+ * @param layout the layout file to check for cyclic dependencies from
+ * @return a collection of layout resources which cannot be included from
+ * the given layout, never null
+ */
+ public Collection<String> getInvalidIncludes(IFile layout) {
+ IProject project = layout.getProject();
+ Reference self = Reference.create(layout);
+
+ // Add anyone who transitively can reach this file via includes.
+ LinkedList<Reference> queue = new LinkedList<Reference>();
+ List<Reference> invalid = new ArrayList<Reference>();
+ queue.add(self);
+ invalid.add(self);
+ Set<String> seen = new HashSet<String>();
+ seen.add(self.getId());
+ while (!queue.isEmpty()) {
+ Reference reference = queue.removeFirst();
+ String refId = reference.getId();
+
+ // Look up both configuration specific includes as well as includes in the
+ // base versions
+ List<String> included = getIncludedBy(refId);
+ if (refId.indexOf('/') != -1) {
+ List<String> baseIncluded = getIncludedBy(reference.getName());
+ if (included == null) {
+ included = baseIncluded;
+ } else if (baseIncluded != null) {
+ included = new ArrayList<String>(included);
+ included.addAll(baseIncluded);
+ }
+ }
+
+ if (included != null && included.size() > 0) {
+ for (String id : included) {
+ if (!seen.contains(id)) {
+ seen.add(id);
+ Reference ref = new Reference(project, id);
+ invalid.add(ref);
+ queue.addLast(ref);
+ }
+ }
+ }
+ }
+
+ List<String> result = new ArrayList<String>();
+ for (Reference reference : invalid) {
+ result.add(reference.getResourceName());
+ }
+
+ return result;
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java
index 5f2450c..0dcd83e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java
@@ -15,10 +15,14 @@
*/
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+
import com.android.ide.common.api.MenuAction;
import com.android.ide.common.api.MenuAction.OrderedChoices;
import com.android.ide.common.api.MenuAction.Separator;
import com.android.ide.common.api.MenuAction.Toggle;
+import com.android.ide.common.layout.BaseViewRule;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
@@ -116,25 +120,58 @@ public class LayoutActionBar extends Composite {
}
List<MenuAction> actions = new ArrayList<MenuAction>();
engine.callAddLayoutActions(actions, parent, selectedNodes);
- addActions(actions);
+
+ // Place actions in the correct order (the actions may come from different
+ // rules and should be merged properly via sorting keys)
+ Collections.sort(actions);
+
+ // Add in actions for the child as well, if there is exactly one.
+ // These are not merged into the parent list of actions; they are appended
+ // at the end.
+ int index = -1;
+ String label = null;
+ if (selectedNodes.size() == 1) {
+ List<MenuAction> itemActions = new ArrayList<MenuAction>();
+ NodeProxy selectedNode = selectedNodes.get(0);
+ engine.callAddLayoutActions(itemActions, selectedNode, null);
+ if (itemActions.size() > 0) {
+ Collections.sort(itemActions);
+
+ if (!(itemActions.get(0) instanceof MenuAction.Separator)) {
+ actions.add(MenuAction.createSeparator(0));
+ }
+ label = selectedNode.getStringAttr(ANDROID_URI, ATTR_ID);
+ if (label != null) {
+ label = BaseViewRule.stripIdPrefix(label);
+ index = actions.size();
+ }
+ actions.addAll(itemActions);
+ }
+ }
+
+ addActions(actions, index, label);
mLayoutToolBar.pack();
mLayoutToolBar.layout();
}
- private void addActions(List<MenuAction> actions) {
+ private void addActions(List<MenuAction> actions, int labelIndex, String label) {
if (actions.size() > 0) {
- // Place actions in the correct order (the actions may come from different
- // rules and should be merged properly via sorting keys)
- Collections.sort(actions);
-
// Flag used to indicate that if there are any actions -after- this, it
// should be separated from this current action (we don't unconditionally
// add a separator at the end of these groups in case there are no more
// actions at the end so that we don't have a trailing separator)
boolean needSeparator = false;
- for (final MenuAction action : actions) {
+ int index = 0;
+ for (MenuAction action : actions) {
+ if (index == labelIndex) {
+ final ToolItem button = new ToolItem(mLayoutToolBar, SWT.PUSH);
+ button.setText(label);
+ needSeparator = false;
+ }
+ index++;
+
if (action instanceof Separator) {
addSeparator(mLayoutToolBar);
needSeparator = false;
@@ -145,7 +182,7 @@ public class LayoutActionBar extends Composite {
}
if (action instanceof MenuAction.OrderedChoices) {
- final MenuAction.OrderedChoices choices = (OrderedChoices) action;
+ MenuAction.OrderedChoices choices = (OrderedChoices) action;
if (!choices.isRadio()) {
addDropdown(choices);
} else {
@@ -339,7 +376,7 @@ public class LayoutActionBar extends Composite {
mZoomFitButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- rescaleToFit();
+ rescaleToFit(true);
}
});
@@ -431,8 +468,8 @@ public class LayoutActionBar extends Composite {
/**
* Reset the canvas scale to best fit (so content is as large as possible without scrollbars)
*/
- void rescaleToFit() {
- mEditor.getCanvasControl().setFitScale();
+ void rescaleToFit(boolean onlyZoomOut) {
+ mEditor.getCanvasControl().setFitScale(onlyZoomOut);
}
boolean rescaleToReal(boolean real) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
index 289831f..c87b669 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
@@ -64,6 +64,8 @@ import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MenuDetectEvent;
+import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
@@ -212,6 +214,11 @@ public class LayoutCanvas extends Canvas {
private final GestureManager mGestureManager = new GestureManager(this);
/**
+ * When set, performs a zoom-to-fit when the next rendering image arrives.
+ */
+ private boolean mZoomFitNextImage;
+
+ /**
* Native clipboard support.
*/
private ClipboardSupport mClipboardSupport;
@@ -235,13 +242,15 @@ public class LayoutCanvas extends Canvas {
if (zoom != null) {
try {
double initialScale = Double.parseDouble(zoom);
- if (initialScale > 0.0) {
+ if (initialScale > 0.1) {
mHScale.setScale(initialScale);
mVScale.setScale(initialScale);
}
} catch (NumberFormatException nfe) {
// Ignore - use zoom=100%
}
+ } else {
+ mZoomFitNextImage = true;
}
}
@@ -252,7 +261,7 @@ public class LayoutCanvas extends Canvas {
// --- Set up graphic overlays
// mOutlineOverlay and mEmptyOverlay are initialized lazily
- mHoverOverlay = new HoverOverlay(mHScale, mVScale);
+ mHoverOverlay = new HoverOverlay(this, mHScale, mVScale);
mHoverOverlay.create(display);
mSelectionOverlay = new SelectionOverlay(this);
mSelectionOverlay.create(display);
@@ -286,6 +295,8 @@ public class LayoutCanvas extends Canvas {
// handle backspace for other platforms as well.
if (e.keyCode == SWT.BS) {
mDeleteAction.run();
+ } else if (e.keyCode == SWT.ESC) {
+ mSelectionManager.selectParent();
} else {
// Zooming actions
char c = e.character;
@@ -294,7 +305,10 @@ public class LayoutCanvas extends Canvas {
if (c == '1' && actionBar.isZoomingAllowed()) {
setScale(1, true);
} else if (c == '0' && actionBar.isZoomingAllowed()) {
- setFitScale();
+ setFitScale(true);
+ } else if (e.keyCode == '0' && (e.stateMask & SWT.MOD2) != 0
+ && actionBar.isZoomingAllowed()) {
+ setFitScale(false);
} else if (c == '+' && actionBar.isZoomingAllowed()) {
actionBar.rescale(1);
} else if (c == '-' && actionBar.isZoomingAllowed()) {
@@ -425,7 +439,7 @@ public class LayoutCanvas extends Canvas {
/**
* Returns the {@link LayoutEditor} associated with this canvas.
*/
- /* package */ LayoutEditor getLayoutEditor() {
+ LayoutEditor getLayoutEditor() {
return mLayoutEditor;
}
@@ -519,6 +533,11 @@ public class LayoutCanvas extends Canvas {
return mClipboardSupport;
}
+ /** Returns the Select All action bound to this canvas */
+ Action getSelectAllAction() {
+ return mSelectAllAction;
+ }
+
/**
* Sets the result of the layout rendering. The result object indicates if the layout
* rendering succeeded. If it did, it contains a bitmap and the objects rectangles.
@@ -534,11 +553,12 @@ public class LayoutCanvas extends Canvas {
* {@link #showInvisibleViews(boolean)}) where individual invisible nodes
* are padded during certain interactions.
*/
- /* package */ void setSession(RenderSession session, Set<UiElementNode> explodedNodes) {
+ /* package */ void setSession(RenderSession session, Set<UiElementNode> explodedNodes,
+ boolean layoutlib5) {
// disable any hover
clearHover();
- mViewHierarchy.setSession(session, explodedNodes);
+ mViewHierarchy.setSession(session, explodedNodes, layoutlib5);
if (mViewHierarchy.isValid() && session != null) {
Image image = mImageOverlay.setImage(session.getImage(), session.isAlphaChannelImage());
@@ -547,6 +567,16 @@ public class LayoutCanvas extends Canvas {
if (image != null) {
mHScale.setSize(image.getImageData().width, getClientArea().width);
mVScale.setSize(image.getImageData().height, getClientArea().height);
+ if (mZoomFitNextImage) {
+ mZoomFitNextImage = false;
+ // Must be run asynchronously because getClientArea() returns 0 bounds
+ // when the editor is being initialized
+ getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ setFitScale(true);
+ }
+ });
+ }
}
}
@@ -563,6 +593,10 @@ public class LayoutCanvas extends Canvas {
}
/* package */ void setScale(double scale, boolean redraw) {
+ if (scale <= 0.0) {
+ scale = 1.0;
+ }
+
if (scale == getScale()) {
return;
}
@@ -578,8 +612,14 @@ public class LayoutCanvas extends Canvas {
AdtPlugin.setFileProperty(mLayoutEditor.getInputFile(), NAME_ZOOM, zoomValue);
}
- /** Scales the canvas to best fit */
- void setFitScale() {
+ /**
+ * Scales the canvas to best fit
+ *
+ * @param onlyZoomOut if true, then the zooming factor will never be larger than 1,
+ * which means that this function will zoom out if necessary to show the
+ * rendered image, but it will never zoom in.
+ */
+ void setFitScale(boolean onlyZoomOut) {
Image image = getImageOverlay().getImage();
if (image != null) {
Rectangle canvasSize = getClientArea();
@@ -610,10 +650,15 @@ public class LayoutCanvas extends Canvas {
vMargin = vDelta / 2;
}
- double hScale = canvasWidth / (double) (sceneWidth - hMargin);
- double vScale = canvasHeight / (double) (sceneHeight - vMargin);
+ double hScale = (canvasWidth - 2 * hMargin) / (double) sceneWidth;
+ double vScale = (canvasHeight - 2 * vMargin) / (double) sceneHeight;
double scale = Math.min(hScale, vScale);
+
+ if (onlyZoomOut) {
+ scale = Math.min(1.0, scale);
+ }
+
setScale(scale, true);
}
}
@@ -724,6 +769,7 @@ public class LayoutCanvas extends Canvas {
if (mShowInvisible == show) {
return;
}
+ mShowInvisible = show;
// Optimization: Avoid doing work when we don't have invisible parents (on show)
// or formerly exploded nodes (on hide).
@@ -733,7 +779,6 @@ public class LayoutCanvas extends Canvas {
return;
}
- mShowInvisible = show;
mLayoutEditor.recomputeLayout();
}
@@ -841,10 +886,23 @@ public class LayoutCanvas extends Canvas {
private void showInclude(String url) {
GraphicalEditorPart graphicalEditor = mLayoutEditor.getGraphicalEditor();
IPath filePath = graphicalEditor.findResourceFile(url);
+ if (filePath == null) {
+ // Should not be possible - if the URL had been bad, then we wouldn't
+ // have been able to render the scene and you wouldn't have been able
+ // to click on it
+ return;
+ }
+
+ // Save the including file, if necessary: without it, the "Show Included In"
+ // facility which is invoked automatically will not work properly if the <include>
+ // tag is not in the saved version of the file, since the outer file is read from
+ // disk rather than from memory.
+ IEditorSite editorSite = graphicalEditor.getEditorSite();
+ IWorkbenchPage page = editorSite.getPage();
+ page.saveEditor(mLayoutEditor, false);
IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
IPath workspacePath = workspace.getLocation();
- IEditorSite editorSite = graphicalEditor.getEditorSite();
if (workspacePath.isPrefixOf(filePath)) {
IPath relativePath = filePath.makeRelativeTo(workspacePath);
IResource xmlFile = workspace.findMember(relativePath);
@@ -899,7 +957,6 @@ public class LayoutCanvas extends Canvas {
IFileStore fileStore = EFS.getLocalFileSystem().getStore(filePath);
// fileStore = fileStore.getChild(names[i]);
if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) {
- IWorkbenchPage page = editorSite.getWorkbenchWindow().getActivePage();
try {
IDE.openEditorOnFileStore(page, fileStore);
return;
@@ -919,7 +976,8 @@ public class LayoutCanvas extends Canvas {
/**
* Returns the layout resource name of this layout
- * @return
+ *
+ * @return the layout resource name of this layout
*/
public String getLayoutResourceName() {
GraphicalEditorPart graphicalEditor = mLayoutEditor.getGraphicalEditor();
@@ -1158,6 +1216,16 @@ public class LayoutCanvas extends Canvas {
new DynamicContextMenu(mLayoutEditor, this, mMenuManager);
Menu menu = mMenuManager.createContextMenu(this);
setMenu(menu);
+
+ // Add listener to detect when the menu is about to be posted, such that
+ // we can sync the selection. Without this, you can right click on something
+ // in the canvas which is NOT selected, and the context menu will show items related
+ // to the selection, NOT the item you clicked on!!
+ addMenuDetectListener(new MenuDetectListener() {
+ public void menuDetected(MenuDetectEvent e) {
+ mSelectionManager.menuClick(e);
+ }
+ });
}
/**
@@ -1173,15 +1241,13 @@ public class LayoutCanvas extends Canvas {
private void setupStaticMenuActions(IMenuManager manager) {
manager.removeAll();
+ manager.add(new SelectionManager.SelectionMenu(mLayoutEditor.getGraphicalEditor()));
+ manager.add(new Separator());
manager.add(mCutAction);
manager.add(mCopyAction);
manager.add(mPasteAction);
-
manager.add(new Separator());
-
manager.add(mDeleteAction);
- manager.add(mSelectAllAction);
-
manager.add(new Separator());
manager.add(new PlayAnimationMenu(this));
manager.add(new Separator());
@@ -1252,7 +1318,7 @@ public class LayoutCanvas extends Canvas {
// A root node requires the Android XMLNS
uiNew.setAttributeValue(
- LayoutConstants.ANDROID_NS_PREFIX,
+ LayoutConstants.ANDROID_NS_NAME,
XmlnsAttributeDescriptor.XMLNS_URI,
SdkConstants.NS_RESOURCES,
true /*override*/);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java
new file mode 100644
index 0000000..2bfa841
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.gle2;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_LAYOUT_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.EXPANDABLE_LIST_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.LAYOUT_PREFIX;
+
+import com.android.ide.common.rendering.api.AdapterBinding;
+import com.android.ide.common.rendering.api.DataBindingItem;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SuppressWarnings("restriction") // XML DOM model
+public class LayoutMetadata {
+ /** The default layout to use for list items in expandable list views */
+ public static final String DEFAULT_EXPANDABLE_LIST_ITEM = "simple_expandable_list_item_2"; //$NON-NLS-1$
+ /** The default layout to use for list items in plain list views */
+ public static final String DEFAULT_LIST_ITEM = "simple_list_item_2"; //$NON-NLS-1$
+
+ /** The string to start metadata comments with */
+ private static final String COMMENT_PROLOGUE = " Preview: ";
+ /** The string to end metadata comments with */
+ private static final String COMMENT_EPILOGUE = " ";
+ /** The property key, included in comments, which references a list item layout */
+ public static final String KEY_LV_ITEM = "listitem"; //$NON-NLS-1$
+ /** The property key, included in comments, which references a list header layout */
+ public static final String KEY_LV_HEADER = "listheader"; //$NON-NLS-1$
+ /** The property key, included in comments, which references a list footer layout */
+ public static final String KEY_LV_FOOTER = "listfooter"; //$NON-NLS-1$
+
+ /** The metadata class is a singleton for now since it has no state of its own */
+ private static final LayoutMetadata sInstance = new LayoutMetadata();
+
+ /** Do not use -- use factory instead */
+ private LayoutMetadata() {
+ }
+
+ /**
+ * Return the {@link LayoutMetadata} instance
+ *
+ * @return the {@link LayoutMetadata} instance
+ */
+ public static LayoutMetadata get() {
+ return sInstance;
+ }
+
+ /**
+ * Returns the given property of the given DOM node, or null
+ *
+ * @param document the document to look up and read lock the model for
+ * @param node the XML node to associate metadata with
+ * @param name the name of the property to look up
+ * @return the value stored with the given node and name, or null
+ */
+ public String getProperty(IDocument document, Node node, String name) {
+ IStructuredModel model = null;
+ try {
+ IModelManager modelManager = StructuredModelManager.getModelManager();
+ model = modelManager.getExistingModelForRead(document);
+
+ Node comment = findComment(node);
+ if (comment != null) {
+ String text = comment.getNodeValue();
+ assert text.startsWith(COMMENT_PROLOGUE);
+ String valuesString = text.substring(COMMENT_PROLOGUE.length());
+ String[] values = valuesString.split(","); //$NON-NLS-1$
+ if (values.length == 1) {
+ valuesString = values[0].trim();
+ if (valuesString.indexOf('\n') != -1) {
+ values = valuesString.split("\n"); //$NON-NLS-1$
+ }
+ }
+ String target = name + '=';
+ for (int j = 0; j < values.length; j++) {
+ String value = values[j].trim();
+ if (value.startsWith(target)) {
+ return value.substring(target.length()).trim();
+ }
+ }
+ }
+
+ return null;
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+ }
+
+ /**
+ * Sets the given property of the given DOM node to a given value, or if null clears
+ * the property.
+ *
+ * @param document the document to look up and write lock the model for
+ * @param node the XML node to associate metadata with
+ * @param name the name of the property to set
+ * @param value the value to store for the given node and name, or null to remove it
+ */
+ public void setProperty(IDocument document, Node node, String name, String value) {
+ // Reserved characters: [,-=]
+ assert name.indexOf('-') == -1;
+ assert value == null || value.indexOf('-') == -1;
+ assert name.indexOf(',') == -1;
+ assert value == null || value.indexOf(',') == -1;
+ assert name.indexOf('=') == -1;
+ assert value == null || value.indexOf('=') == -1;
+
+ IStructuredModel model = null;
+ try {
+ IModelManager modelManager = StructuredModelManager.getModelManager();
+ model = modelManager.getExistingModelForEdit(document);
+ if (model instanceof IDOMModel) {
+ IDOMModel domModel = (IDOMModel) model;
+ Document domDocument = domModel.getDocument();
+ assert node.getOwnerDocument() == domDocument;
+ }
+
+ Document doc = node.getOwnerDocument();
+ Node commentNode = findComment(node);
+
+ String commentText = null;
+ if (commentNode != null) {
+ String text = commentNode.getNodeValue();
+ assert text.startsWith(COMMENT_PROLOGUE);
+ String valuesString = text.substring(COMMENT_PROLOGUE.length());
+ String[] values = valuesString.split(","); //$NON-NLS-1$
+ if (values.length == 1) {
+ valuesString = values[0].trim();
+ if (valuesString.indexOf('\n') != -1) {
+ values = valuesString.split("\n"); //$NON-NLS-1$
+ }
+ }
+ String target = name + '=';
+ List<String> preserve = new ArrayList<String>();
+ for (int j = 0; j < values.length; j++) {
+ String v = values[j].trim();
+ if (v.length() == 0) {
+ continue;
+ }
+ if (!v.startsWith(target)) {
+ preserve.add(v.trim());
+ }
+ }
+ if (value != null) {
+ preserve.add(name + '=' + value.trim());
+ }
+ if (preserve.size() > 0) {
+ if (preserve.size() > 1) {
+ Collections.sort(preserve);
+ String firstLineIndent = AndroidXmlEditor.getIndent(document, commentNode);
+ String oneIndentLevel = " "; //$NON-NLS-1$
+ StringBuilder sb = new StringBuilder();
+ sb.append(COMMENT_PROLOGUE);
+ sb.append('\n');
+ for (String s : preserve) {
+ sb.append(firstLineIndent);
+ sb.append(oneIndentLevel);
+ sb.append(s);
+ sb.append('\n');
+ }
+ sb.append(firstLineIndent);
+ sb.append(COMMENT_EPILOGUE);
+ commentText = sb.toString();
+ } else {
+ commentText = COMMENT_PROLOGUE + preserve.get(0) + COMMENT_EPILOGUE;
+ }
+ }
+ } else if (value != null) {
+ commentText = COMMENT_PROLOGUE + name + '=' + value + COMMENT_EPILOGUE;
+ }
+
+ if (commentText == null) {
+ if (commentNode != null) {
+ // Remove the comment, along with surrounding whitespace if applicable
+ Node previous = commentNode.getPreviousSibling();
+ if (previous != null && previous.getNodeType() == Node.TEXT_NODE) {
+ String text = previous.getNodeValue();
+ if (text.trim().length() == 0) {
+ node.removeChild(previous);
+ }
+ }
+ node.removeChild(commentNode);
+ Node first = node.getFirstChild();
+ if (first != null && first.getNextSibling() == null
+ && first.getNodeType() == Node.TEXT_NODE) {
+ String text = first.getNodeValue();
+ if (text.trim().length() == 0) {
+ node.removeChild(first);
+ }
+ }
+ }
+ return;
+ }
+
+ if (commentNode != null) {
+ commentNode.setNodeValue(commentText);
+ } else {
+ commentNode = doc.createComment(commentText);
+ String firstLineIndent = AndroidXmlEditor.getIndent(document, node);
+ Node firstChild = node.getFirstChild();
+ boolean indentAfter = firstChild == null
+ || firstChild.getNodeType() != Node.TEXT_NODE
+ || firstChild.getNodeValue().indexOf('\n') == -1;
+ String oneIndentLevel = " "; //$NON-NLS-1$
+ node.insertBefore(doc.createTextNode('\n' + firstLineIndent + oneIndentLevel),
+ firstChild);
+ node.insertBefore(commentNode, firstChild);
+ if (indentAfter) {
+ node.insertBefore(doc.createTextNode('\n' + firstLineIndent), firstChild);
+ }
+ }
+ } finally {
+ if (model != null) {
+ model.releaseFromEdit();
+ }
+ }
+ }
+
+ /** Finds the comment node associated with the given node, or null if not found */
+ private Node findComment(Node node) {
+ NodeList children = node.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.COMMENT_NODE) {
+ String text = child.getNodeValue();
+ if (text.startsWith(COMMENT_PROLOGUE)) {
+ return child;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the given property of the given DOM node, or null
+ *
+ * @param editor the editor associated with the property
+ * @param node the XML node to associate metadata with
+ * @param name the name of the property to look up
+ * @return the value stored with the given node and name, or null
+ */
+ public String getProperty(AndroidXmlEditor editor, Node node, String name) {
+ IDocument document = editor.getStructuredSourceViewer().getDocument();
+ return getProperty(document, node, name);
+ }
+
+ /**
+ * Sets the given property of the given DOM node to a given value, or if null clears
+ * the property.
+ *
+ * @param editor the editor associated with the property
+ * @param node the XML node to associate metadata with
+ * @param name the name of the property to set
+ * @param value the value to store for the given node and name, or null to remove it
+ */
+ public void setProperty(AndroidXmlEditor editor, Node node, String name, String value) {
+ IDocument document = editor.getStructuredSourceViewer().getDocument();
+ setProperty(document, node, name, value);
+ }
+
+ /** Strips out @layout/ or @android:layout/ from the given layout reference */
+ private static String stripLayoutPrefix(String layout) {
+ if (layout.startsWith(ANDROID_LAYOUT_PREFIX)) {
+ layout = layout.substring(ANDROID_LAYOUT_PREFIX.length());
+ } else if (layout.startsWith(LAYOUT_PREFIX)) {
+ layout = layout.substring(LAYOUT_PREFIX.length());
+ }
+
+ return layout;
+ }
+
+ /**
+ * Creates an {@link AdapterBinding} for the given view object, or null if the user
+ * has not yet chosen a target layout to use for the given AdapterView.
+ *
+ * @param viewObject the view object to create an adapter binding for
+ * @param uiNode the ui node corresponding to the view object
+ * @return a binding, or null
+ */
+ public AdapterBinding getNodeBinding(Object viewObject, UiViewElementNode uiNode) {
+ AndroidXmlEditor editor = uiNode.getEditor();
+ if (editor != null) {
+ Node xmlNode = uiNode.getXmlNode();
+
+ String header = getProperty(editor, xmlNode, KEY_LV_HEADER);
+ String footer = getProperty(editor, xmlNode, KEY_LV_FOOTER);
+ String layout = getProperty(editor, xmlNode, KEY_LV_ITEM);
+ if (layout != null || header != null || footer != null) {
+ AdapterBinding binding = new AdapterBinding(12);
+
+ if (header != null) {
+ boolean isFramework = header.startsWith(ANDROID_LAYOUT_PREFIX);
+ binding.addHeader(new ResourceReference(stripLayoutPrefix(header),
+ isFramework));
+ }
+
+ if (footer != null) {
+ boolean isFramework = footer.startsWith(ANDROID_LAYOUT_PREFIX);
+ binding.addFooter(new ResourceReference(stripLayoutPrefix(footer),
+ isFramework));
+ }
+
+ if (layout != null) {
+ boolean isFramework = layout.startsWith(ANDROID_LAYOUT_PREFIX);
+ if (isFramework) {
+ layout = layout.substring(ANDROID_LAYOUT_PREFIX.length());
+ } else if (layout.startsWith(LAYOUT_PREFIX)) {
+ layout = layout.substring(LAYOUT_PREFIX.length());
+ }
+
+ binding.addItem(new DataBindingItem(layout, isFramework, 1));
+ } else if (viewObject != null) {
+ String listFqcn = ProjectCallback.getListViewFqcn(viewObject.getClass());
+ if (listFqcn != null) {
+ if (listFqcn.endsWith(EXPANDABLE_LIST_VIEW)) {
+ binding.addItem(
+ new DataBindingItem(DEFAULT_EXPANDABLE_LIST_ITEM,
+ true /* isFramework */, 1));
+ } else {
+ binding.addItem(
+ new DataBindingItem(DEFAULT_LIST_ITEM,
+ true /* isFramework */, 1));
+ }
+ }
+ } else {
+ binding.addItem(
+ new DataBindingItem(DEFAULT_LIST_ITEM,
+ true /* isFramework */, 1));
+ }
+ return binding;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ListViewTypeMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ListViewTypeMenu.java
new file mode 100644
index 0000000..e522957
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ListViewTypeMenu.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.gle2;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_LAYOUT_PREFIX;
+import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata.KEY_LV_FOOTER;
+import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata.KEY_LV_HEADER;
+import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata.KEY_LV_ITEM;
+
+import com.android.ide.common.rendering.api.Capability;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+import com.android.ide.eclipse.adt.internal.resources.CyclicDependencyValidator;
+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.ui.ResourceChooser;
+import com.android.resources.ResourceType;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.w3c.dom.Node;
+
+/**
+ * "Preview List Content" context menu which lists available data types and layouts
+ * the user can choose to view the ListView as.
+ */
+public class ListViewTypeMenu extends SubmenuAction {
+ /** Associated canvas */
+ private final LayoutCanvas mCanvas;
+
+ /**
+ * Creates a "Preview List Content" menu
+ *
+ * @param canvas associated canvas
+ */
+ public ListViewTypeMenu(LayoutCanvas canvas) {
+ super("Preview List Content");
+ mCanvas = canvas;
+ }
+
+ @Override
+ protected void addMenuItems(Menu menu) {
+ GraphicalEditorPart graphicalEditor = mCanvas.getLayoutEditor().getGraphicalEditor();
+ if (graphicalEditor.renderingSupports(Capability.ADAPTER_BINDING)) {
+ IAction action = new PickLayoutAction("Choose Layout...", KEY_LV_ITEM);
+ new ActionContributionItem(action).fill(menu, -1);
+ new Separator().fill(menu, -1);
+
+ String selected = getSelectedLayout();
+ if (selected != null) {
+ if (selected.startsWith(ANDROID_LAYOUT_PREFIX)) {
+ selected = selected.substring(ANDROID_LAYOUT_PREFIX.length());
+ }
+ }
+ action = new SetListTypeAction("Simple List Item",
+ "simple_list_item_1", selected); //$NON-NLS-1$
+ new ActionContributionItem(action).fill(menu, -1);
+ action = new SetListTypeAction("Simple 2-Line List Item",
+ "simple_list_item_2", //$NON-NLS-1$
+ selected);
+ new ActionContributionItem(action).fill(menu, -1);
+ action = new SetListTypeAction("Checked List Item",
+ "simple_list_item_checked", //$NON-NLS-1$
+ selected);
+ new ActionContributionItem(action).fill(menu, -1);
+ action = new SetListTypeAction("Single Choice List Item",
+ "simple_list_item_single_choice", //$NON-NLS-1$
+ selected);
+ new ActionContributionItem(action).fill(menu, -1);
+ action = new SetListTypeAction("Multiple Choice List Item",
+ "simple_list_item_multiple_choice", //$NON-NLS-1$
+ selected);
+ new Separator().fill(menu, -1);
+ action = new SetListTypeAction("Simple Expandable List Item",
+ "simple_expandable_list_item_1", selected); //$NON-NLS-1$
+ new ActionContributionItem(action).fill(menu, -1);
+ action = new SetListTypeAction("Simple 2-Line Expandable List Item",
+ "simple_expandable_list_item_2", //$NON-NLS-1$
+ selected);
+ new ActionContributionItem(action).fill(menu, -1);
+
+ new Separator().fill(menu, -1);
+ action = new PickLayoutAction("Choose Header...", KEY_LV_HEADER);
+ new ActionContributionItem(action).fill(menu, -1);
+ action = new PickLayoutAction("Choose Footer...", KEY_LV_FOOTER);
+ new ActionContributionItem(action).fill(menu, -1);
+
+ } else {
+ // Should we just hide the menu item instead?
+ addDisabledMessageItem(
+ "Not supported for this SDK version; try changing the Render Target");
+ }
+ }
+
+ private class SetListTypeAction extends Action {
+ private final String mLayout;
+
+ public SetListTypeAction(String title, String layout, String selected) {
+ super(title, IAction.AS_RADIO_BUTTON);
+ mLayout = layout;
+
+ if (layout.equals(selected)) {
+ setChecked(true);
+ }
+ }
+
+ @Override
+ public void run() {
+ setNewType(KEY_LV_ITEM, ANDROID_LAYOUT_PREFIX + mLayout);
+ }
+ }
+
+ /**
+ * Action which brings up the "Create new XML File" wizard, pre-selected with the
+ * animation category
+ */
+ private class PickLayoutAction extends Action {
+ private final String mType;
+
+ public PickLayoutAction(String title, String type) {
+ super(title, IAction.AS_PUSH_BUTTON);
+ mType = type;
+ }
+
+ @Override
+ public void run() {
+ LayoutEditor editor = mCanvas.getLayoutEditor();
+ IProject project = editor.getProject();
+ // get the resource repository for this project and the system resources.
+ ResourceRepository projectRepository = ResourceManager.getInstance()
+ .getProjectResources(project);
+ Shell shell = mCanvas.getShell();
+
+ AndroidTargetData data = editor.getTargetData();
+ ResourceRepository systemRepository = data.getFrameworkResources();
+
+ ResourceChooser dlg = new ResourceChooser(project,
+ ResourceType.LAYOUT, projectRepository,
+ systemRepository, shell);
+
+ IInputValidator validator = CyclicDependencyValidator.create(editor.getInputFile());
+
+ if (validator != null) {
+ // Ensure wide enough to accommodate validator error message
+ dlg.setSize(70, 10);
+ dlg.setInputValidator(validator);
+ }
+
+ String layout = getSelectedLayout();
+ if (layout != null) {
+ dlg.setCurrentResource(layout);
+ }
+
+ int result = dlg.open();
+ if (result == ResourceChooser.CLEAR_RETURN_CODE) {
+ setNewType(mType, null);
+ } else if (result == Window.OK) {
+ String newType = dlg.getCurrentResource();
+ setNewType(mType, newType);
+ }
+ }
+ }
+
+ private String getSelectedLayout() {
+ String layout = null;
+ LayoutEditor editor = mCanvas.getLayoutEditor();
+ LayoutMetadata metadata = LayoutMetadata.get();
+ SelectionManager selectionManager = mCanvas.getSelectionManager();
+ for (SelectionItem item : selectionManager.getSelections()) {
+ UiViewElementNode node = item.getViewInfo().getUiViewNode();
+
+ Node xmlNode = node.getXmlNode();
+ layout = metadata.getProperty(editor, xmlNode, KEY_LV_ITEM);
+ if (layout != null) {
+ return layout;
+ }
+ }
+ return null;
+ }
+
+ public void setNewType(String type, String layout) {
+ LayoutEditor editor = mCanvas.getLayoutEditor();
+ GraphicalEditorPart graphicalEditor = editor.getGraphicalEditor();
+ LayoutMetadata metadata = LayoutMetadata.get();
+ SelectionManager selectionManager = mCanvas.getSelectionManager();
+
+ for (SelectionItem item : selectionManager.getSelections()) {
+ UiViewElementNode node = item.getViewInfo().getUiViewNode();
+ Node xmlNode = node.getXmlNode();
+ metadata.setProperty(editor, xmlNode, type, layout);
+ }
+
+ // Refresh
+ graphicalEditor.recomputeLayout();
+ mCanvas.redraw();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
index 07ab41c..a2cdbe6 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
@@ -24,6 +24,9 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TransferData;
@@ -120,6 +123,15 @@ public class MoveGesture extends DropGesture {
return Collections.<Overlay> singletonList(mOverlay);
}
+ @Override
+ public void end(ControlPoint pos, boolean canceled) {
+ super.end(pos, canceled);
+
+ // Ensure that the outline is back to showing the current selection, since during
+ // a drag gesture we temporarily set it to show the current target node instead.
+ mCanvas.getSelectionManager().syncOutlineSelection();
+ }
+
/*
* The cursor has entered the drop target boundaries.
* {@inheritDoc}
@@ -658,6 +670,26 @@ public class MoveGesture extends DropGesture {
if (needRedraw || (mFeedback != null && mFeedback.requestPaint)) {
mCanvas.redraw();
}
+
+ // Update outline to show the target node there
+ OutlinePage outline = mCanvas.getOutlinePage();
+ TreeSelection newSelection = TreeSelection.EMPTY;
+ if (mCurrentView != null) {
+ // Find the view corresponding to the target node. The current view can be a leaf
+ // view whereas the target node is always a parent layout.
+ if (mCurrentView.getUiViewNode() != mTargetNode.getNode()) {
+ mCurrentView = mCurrentView.getParent();
+ }
+ if (mCurrentView != null && mCurrentView.getUiViewNode() == mTargetNode.getNode()) {
+ TreePath treePath = SelectionManager.getTreePath(mCurrentView);
+ newSelection = new TreeSelection(treePath);
+ }
+ }
+
+ ISelection currentSelection = outline.getSelection();
+ if (currentSelection == null || !currentSelection.equals(newSelection)) {
+ outline.setSelection(newSelection);
+ }
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java
index f591149..d1ca8a9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java
@@ -155,7 +155,7 @@ import java.util.Set;
}
// Select the newly dropped nodes
final SelectionManager selectionManager = canvas.getSelectionManager();
- selectionManager.updateOutlineSelection(added);
+ selectionManager.setOutlineSelection(added);
canvas.redraw();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
index 02f98b1..9c27f6e 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
@@ -17,11 +17,13 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION;
import static com.android.ide.common.layout.LayoutConstants.ATTR_SRC;
import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
import static com.android.ide.common.layout.LayoutConstants.DRAWABLE_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.LAYOUT_PREFIX;
-
+import static com.android.ide.common.layout.LayoutConstants.LINEAR_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL;
import static org.eclipse.jface.viewers.StyledString.QUALIFIER_STYLER;
import com.android.annotations.VisibleForTesting;
@@ -469,7 +471,20 @@ public class OutlinePage extends ContentOutlinePage
UiElementNode node = (UiElementNode) element;
ElementDescriptor desc = node.getDescriptor();
if (desc != null) {
- Image img = desc.getIcon();
+ Image img = null;
+ // Special case for the common case of vertical linear layouts:
+ // show vertical linear icon (the default icon shows horizontal orientation)
+ if (desc.getUiName().equals(LINEAR_LAYOUT)) {
+ Element e = (Element) node.getXmlNode();
+ if (VALUE_VERTICAL.equals(e.getAttributeNS(ANDROID_URI,
+ ATTR_ORIENTATION))) {
+ IconFactory factory = IconFactory.getInstance();
+ img = factory.getIcon("VerticalLinearLayout"); //$NON-NLS-1$
+ }
+ }
+ if (img == null) {
+ img = desc.getGenericIcon();
+ }
if (img != null) {
if (node.hasError()) {
return new ErrorImageComposite(img).createImage();
@@ -589,6 +604,8 @@ public class OutlinePage extends ContentOutlinePage
mMenuManager.add(mMoveDownAction);
mMenuManager.add(new Separator());
+ mMenuManager.add(new SelectionManager.SelectionMenu(mGraphicalEditorPart));
+ mMenuManager.add(new Separator());
final String prefix = LayoutCanvas.PREFIX_CANVAS_ACTION;
mMenuManager.add(new DelegateAction(prefix + ActionFactory.CUT.getId()));
mMenuManager.add(new DelegateAction(prefix + ActionFactory.COPY.getId()));
@@ -597,7 +614,6 @@ public class OutlinePage extends ContentOutlinePage
mMenuManager.add(new Separator());
mMenuManager.add(new DelegateAction(prefix + ActionFactory.DELETE.getId()));
- mMenuManager.add(new DelegateAction(prefix + ActionFactory.SELECT_ALL.getId()));
mMenuManager.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
@@ -790,7 +806,7 @@ public class OutlinePage extends ContentOutlinePage
}
}
- selectionManager.updateOutlineSelection(added);
+ selectionManager.setOutlineSelection(added);
}
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java
index f8b3109..2227531 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java
@@ -23,24 +23,29 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Rect;
import com.android.ide.common.api.MenuAction.Toggle;
+import com.android.ide.common.api.Rect;
import com.android.ide.common.rendering.LayoutLibrary;
import com.android.ide.common.rendering.api.Capability;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.CustomViewDescriptorService;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.editors.ui.DecorComposite;
import com.android.ide.eclipse.adt.internal.editors.ui.IDecorContent;
@@ -50,7 +55,6 @@ import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.SdkConstants;
import com.android.util.Pair;
import org.eclipse.jface.action.Action;
@@ -69,8 +73,11 @@ import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
+import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
@@ -79,10 +86,15 @@ import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -91,6 +103,7 @@ import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -123,7 +136,7 @@ public class PaletteControl extends Composite {
*/
public static class PaletteDecor implements IDecorContent {
private final GraphicalEditorPart mEditorPart;
- private Control mControl;
+ private PaletteControl mControl;
public PaletteDecor(GraphicalEditorPart editor) {
mEditorPart = editor;
@@ -144,6 +157,22 @@ public class PaletteControl extends Composite {
public Control getControl() {
return mControl;
}
+
+ public void createToolbarItems(final ToolBar toolbar) {
+ final ToolItem popupMenuItem = new ToolItem(toolbar, SWT.PUSH);
+ popupMenuItem.setToolTipText("View Menu");
+ popupMenuItem.setImage(IconFactory.getInstance().getIcon("view_menu"));
+ popupMenuItem.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Rectangle bounds = popupMenuItem.getBounds();
+ // Align menu horizontally with the toolbar button and
+ // vertically with the bottom of the toolbar
+ Point point = toolbar.toDisplay(bounds.x, bounds.y + bounds.height);
+ mControl.showMenu(point.x, point.y);
+ }
+ });
+ }
}
/**
@@ -417,7 +446,17 @@ public class PaletteControl extends Composite {
categoryToItems.put(category, categoryItems);
}
- if (expandedCategories == null && headers.size() > 0) {
+ headers.add("Custom & Library Views");
+
+ // Set the categories to expand the first item if
+ // (1) we don't have a previously selected category, or
+ // (2) there's just one category anyway, or
+ // (3) the set of categories have changed so our previously selected category
+ // doesn't exist anymore (can happen when you toggle "Show Categories")
+ if ((expandedCategories == null && headers.size() > 0) || headers.size() == 1 ||
+ (expandedCategories != null && expandedCategories.size() >= 1
+ && !headers.contains(
+ expandedCategories.iterator().next().replace("&&", "&")))) { //$NON-NLS-1$ //$NON-NLS-2$
// Expand the first category if we don't have a previous selection (e.g. refresh)
expandedCategories = Collections.singleton(headers.get(0));
}
@@ -430,10 +469,45 @@ public class PaletteControl extends Composite {
mAccordion = new AccordionControl(this, SWT.NONE, headers, fillVertical, wrap,
expandedCategories) {
@Override
- protected Composite createChildContainer(Composite parent) {
- Composite composite = super.createChildContainer(parent);
- if (mPaletteMode.isPreview() && mBackground != null) {
- composite.setBackground(mBackground);
+ protected Composite createChildContainer(Composite parent, Object header, int style) {
+ assert categoryToItems != null;
+ List<ViewElementDescriptor> list = categoryToItems.get(header);
+ final Composite composite;
+ if (list == null) {
+ assert header.equals("Custom & Library Views");
+
+ Composite wrapper = new Composite(parent, SWT.NONE);
+ GridLayout gridLayout = new GridLayout(1, false);
+ gridLayout.marginWidth = gridLayout.marginHeight = 0;
+ gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 0;
+ gridLayout.marginBottom = 3;
+ wrapper.setLayout(gridLayout);
+ if (mPaletteMode.isPreview() && mBackground != null) {
+ wrapper.setBackground(mBackground);
+ }
+ composite = super.createChildContainer(wrapper, header,
+ style | SWT.NO_BACKGROUND);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+
+ Button refreshButton = new Button(wrapper, SWT.PUSH | SWT.FLAT);
+ refreshButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER,
+ false, false, 1, 1));
+ refreshButton.setText("Refresh");
+ refreshButton.setImage(IconFactory.getInstance().getIcon("refresh")); //$NON-NLS-1$
+ refreshButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ CustomViewFinder finder = CustomViewFinder.get(mEditor.getProject());
+ finder.refresh(new ViewFinderListener(composite));
+ }
+ });
+
+ wrapper.layout(true);
+ } else {
+ composite = super.createChildContainer(parent, header, style);
+ if (mPaletteMode.isPreview() && mBackground != null) {
+ composite.setBackground(mBackground);
+ }
}
addMenu(composite);
return composite;
@@ -442,8 +516,14 @@ public class PaletteControl extends Composite {
protected void createChildren(Composite parent, Object header) {
assert categoryToItems != null;
List<ViewElementDescriptor> list = categoryToItems.get(header);
- for (ViewElementDescriptor desc : list) {
- createItem(parent, desc);
+ if (list == null) {
+ assert header.equals("Custom & Library Views");
+ addCustomItems(parent);
+ return;
+ } else {
+ for (ViewElementDescriptor desc : list) {
+ createItem(parent, desc);
+ }
}
}
};
@@ -465,6 +545,47 @@ public class PaletteControl extends Composite {
layout(true);
}
+ protected void addCustomItems(final Composite parent) {
+ final CustomViewFinder finder = CustomViewFinder.get(mEditor.getProject());
+ Collection<String> allViews = finder.getAllViews();
+ if (allViews == null) { // Not yet initialized: trigger an async refresh
+ finder.refresh(new ViewFinderListener(parent));
+ return;
+ }
+
+ // Remove previous content
+ for (Control c : parent.getChildren()) {
+ c.dispose();
+ }
+
+ // Add new views
+ for (final String fqcn : allViews) {
+ CustomViewDescriptorService service = CustomViewDescriptorService.getInstance();
+ ViewElementDescriptor desc = service.getDescriptor(mEditor.getProject(), fqcn);
+ if (desc == null) {
+ // The descriptor lookup performs validation steps of the class, and may
+ // in some cases determine that this is not a view and will return null;
+ // guard against that.
+ continue;
+ }
+
+ Control item = createItem(parent, desc);
+
+ // Add control-click listener on custom view items to you can warp to
+ if (item instanceof IconTextItem) {
+ IconTextItem it = (IconTextItem) item;
+ it.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if ((e.stateMask & SWT.MOD1) != 0) {
+ AdtPlugin.openJavaClass(mEditor.getProject(), fqcn);
+ }
+ }
+ });
+ }
+ }
+ }
+
/* package */ GraphicalEditorPart getEditor() {
return mEditor;
}
@@ -525,7 +646,7 @@ public class PaletteControl extends Composite {
break;
}
case ICON_ONLY: {
- item = new ImageControl(parent, SWT.None, desc.getIcon());
+ item = new ImageControl(parent, SWT.None, desc.getGenericIcon());
item.setToolTipText(desc.getUiName());
break;
}
@@ -559,7 +680,7 @@ public class PaletteControl extends Composite {
mMouseIn = false;
setText(desc.getUiName());
- setImage(desc.getIcon());
+ setImage(desc.getGenericIcon());
setToolTipText(desc.getTooltip());
addMouseTrackListener(this);
}
@@ -613,13 +734,16 @@ public class PaletteControl extends Composite {
createDragImage(e);
if (mImage != null && !mIsPlaceholder) {
- ImageData data = mImage.getImageData();
+ int imageWidth = mImageLayoutBounds.width;
+ int imageHeight = mImageLayoutBounds.height;
+ assert mImageLayoutBounds.x == 0;
+ assert mImageLayoutBounds.y == 0;
LayoutCanvas canvas = mEditor.getCanvasControl();
double scale = canvas.getScale();
- int x = -data.width / 2;
- int y = -data.height / 2;
- int width = (int) (data.width / scale);
- int height = (int) (data.height / scale);
+ int x = -imageWidth / 2;
+ int y = -imageHeight / 2;
+ int width = (int) (imageWidth / scale);
+ int height = (int) (imageHeight / scale);
bounds = new Rect(0, 0, width, height);
dragBounds = new Rect(x, y, width, height);
}
@@ -629,6 +753,10 @@ public class PaletteControl extends Composite {
null /* parentFqcn */,
bounds /* bounds */,
null /* parentBounds */);
+ if (mDesc instanceof PaletteMetadataDescriptor) {
+ PaletteMetadataDescriptor pm = (PaletteMetadataDescriptor) mDesc;
+ pm.initializeNew(se);
+ }
mElements = new SimpleElement[] { se };
// Register this as the current dragged data
@@ -681,8 +809,10 @@ public class PaletteControl extends Composite {
/** Amount of alpha to multiply into the image (divided by 256) */
private static final int IMG_ALPHA = 216;
- /** The image shown by the drag source effect */
+ /** The image shown during the drag */
private Image mImage;
+ /** The non-effect bounds of the drag image */
+ private Rectangle mImageLayoutBounds;
/**
* If true, the image is a preview of the view, and if not it is a "fallback"
@@ -691,7 +821,14 @@ public class PaletteControl extends Composite {
private boolean mIsPlaceholder;
private void createDragImage(DragSourceEvent event) {
- mImage = renderPreview();
+ Pair<Image, Rectangle> preview = renderPreview();
+ if (preview != null) {
+ mImage = preview.getFirst();
+ mImageLayoutBounds = preview.getSecond();
+ } else {
+ mImage = null;
+ mImageLayoutBounds = null;
+ }
mIsPlaceholder = mImage == null;
if (mIsPlaceholder) {
@@ -737,8 +874,17 @@ public class PaletteControl extends Composite {
}
}
- /** Performs the actual rendering of the descriptor into an image */
- private Image renderPreview() {
+ /**
+ * Performs the actual rendering of the descriptor into an image and returns the
+ * image as well as the layout bounds of the image (not including drop shadow etc)
+ */
+ private Pair<Image, Rectangle> renderPreview() {
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+ RenderMode renderMode = repository.getRenderMode(mDesc.getFullClassName());
+ if (renderMode == RenderMode.SKIP) {
+ return null;
+ }
+
// Create blank XML document
Document document = null;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
@@ -764,14 +910,21 @@ public class PaletteControl extends Composite {
attr.setValue(ANDROID_URI);
element.getAttributes().setNamedItemNS(attr);
- element.setAttributeNS(SdkConstants.NS_RESOURCES,
+ element.setAttributeNS(ANDROID_URI,
ATTR_LAYOUT_WIDTH, VALUE_WRAP_CONTENT);
- element.setAttributeNS(SdkConstants.NS_RESOURCES,
+ element.setAttributeNS(ANDROID_URI,
ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT);
// This doesn't apply to all, but doesn't seem to cause harm and makes for a
// better experience with text-oriented views like buttons and texts
- element.setAttributeNS(SdkConstants.NS_RESOURCES, ATTR_TEXT, mDesc.getUiName());
+ element.setAttributeNS(ANDROID_URI, ATTR_TEXT,
+ DescriptorsUtils.getBasename(mDesc.getUiName()));
+
+ // Is this a palette variation?
+ if (mDesc instanceof PaletteMetadataDescriptor) {
+ PaletteMetadataDescriptor pm = (PaletteMetadataDescriptor) mDesc;
+ pm.initializeNew(element);
+ }
document.appendChild(element);
@@ -799,7 +952,7 @@ public class PaletteControl extends Composite {
UiViewElementNode childUiNode = (UiViewElementNode) child;
NodeProxy childNode = nodeFactory.create(childUiNode);
canvas.getRulesEngine().callCreateHooks(layoutEditor,
- null, childNode, InsertType.CREATE);
+ null, childNode, InsertType.CREATE_PREVIEW);
}
Integer overrideBgColor = null;
@@ -878,6 +1031,8 @@ public class PaletteControl extends Composite {
}
if (cropped != null) {
+ int width = initialCrop != null ? initialCrop.w : cropped.getWidth();
+ int height = initialCrop != null ? initialCrop.h : cropped.getHeight();
boolean needsContrast = hasTransparency
&& !ImageUtils.containsDarkPixels(cropped);
cropped = ImageUtils.createDropShadow(cropped,
@@ -893,7 +1048,8 @@ public class PaletteControl extends Composite {
Display display = getDisplay();
int alpha = (!hasTransparency || !needsContrast) ? IMG_ALPHA : -1;
Image swtImage = SwtUtils.convertToSwt(display, cropped, true, alpha);
- return swtImage;
+ Rectangle imageBounds = new Rectangle(0, 0, width, height);
+ return Pair.of(swtImage, imageBounds);
}
}
}
@@ -969,9 +1125,11 @@ public class PaletteControl extends Composite {
final static int TOGGLE_ALPHABETICAL = 2;
final static int TOGGLE_AUTO_CLOSE = 3;
final static int REFRESH = 4;
+ final static int RESET = 5;
ToggleViewOptionAction(String title, int action, boolean checked) {
- super(title, action == REFRESH ? IAction.AS_PUSH_BUTTON : IAction.AS_CHECK_BOX);
+ super(title, (action == REFRESH || action == RESET) ? IAction.AS_PUSH_BUTTON
+ : IAction.AS_CHECK_BOX);
mAction = action;
if (checked) {
setChecked(checked);
@@ -997,6 +1155,13 @@ public class PaletteControl extends Composite {
mPreviewIconFactory.refresh();
refreshPalette();
break;
+ case RESET:
+ mAlphabetical = false;
+ mCategories = true;
+ mAutoClose = true;
+ mPaletteMode = PaletteMode.SMALL_PREVIEW;
+ refreshPalette();
+ break;
}
savePaletteMode();
}
@@ -1005,41 +1170,63 @@ public class PaletteControl extends Composite {
private void addMenu(Control control) {
control.addMenuDetectListener(new MenuDetectListener() {
public void menuDetected(MenuDetectEvent e) {
- MenuManager manager = new MenuManager() {
- @Override
- public boolean isDynamic() {
- return true;
- }
- };
- boolean previews = previewsAvailable();
- for (PaletteMode mode : PaletteMode.values()) {
- if (mode.isPreview() && !previews) {
- continue;
- }
- manager.add(new PaletteModeAction(mode));
- }
- if (mPaletteMode.isPreview()) {
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Refresh Previews",
- ToggleViewOptionAction.REFRESH,
- false));
- }
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Show Categories",
- ToggleViewOptionAction.TOGGLE_CATEGORY,
- mCategories));
- manager.add(new ToggleViewOptionAction("Sort Alphabetically",
- ToggleViewOptionAction.TOGGLE_ALPHABETICAL,
- mAlphabetical));
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Auto Close Previous",
- ToggleViewOptionAction.TOGGLE_AUTO_CLOSE,
- mAutoClose));
- Menu menu = manager.createContextMenu(PaletteControl.this);
- Point point = new Point(e.x, e.y);
- menu.setLocation(point.x, point.y);
- menu.setVisible(true);
+ showMenu(e.x, e.y);
}
});
}
+
+ private void showMenu(int x, int y) {
+ MenuManager manager = new MenuManager() {
+ @Override
+ public boolean isDynamic() {
+ return true;
+ }
+ };
+ boolean previews = previewsAvailable();
+ for (PaletteMode mode : PaletteMode.values()) {
+ if (mode.isPreview() && !previews) {
+ continue;
+ }
+ manager.add(new PaletteModeAction(mode));
+ }
+ if (mPaletteMode.isPreview()) {
+ manager.add(new Separator());
+ manager.add(new ToggleViewOptionAction("Refresh Previews",
+ ToggleViewOptionAction.REFRESH,
+ false));
+ }
+ manager.add(new Separator());
+ manager.add(new ToggleViewOptionAction("Show Categories",
+ ToggleViewOptionAction.TOGGLE_CATEGORY,
+ mCategories));
+ manager.add(new ToggleViewOptionAction("Sort Alphabetically",
+ ToggleViewOptionAction.TOGGLE_ALPHABETICAL,
+ mAlphabetical));
+ manager.add(new Separator());
+ manager.add(new ToggleViewOptionAction("Auto Close Previous",
+ ToggleViewOptionAction.TOGGLE_AUTO_CLOSE,
+ mAutoClose));
+ manager.add(new Separator());
+ manager.add(new ToggleViewOptionAction("Reset",
+ ToggleViewOptionAction.RESET,
+ false));
+
+ Menu menu = manager.createContextMenu(PaletteControl.this);
+ menu.setLocation(x, y);
+ menu.setVisible(true);
+ }
+
+ private final class ViewFinderListener implements CustomViewFinder.Listener {
+ private final Composite mParent;
+
+ private ViewFinderListener(Composite parent) {
+ this.mParent = parent;
+ }
+
+ public void viewsUpdated(Collection<String> customViews,
+ Collection<String> thirdPartyViews) {
+ addCustomItems(mParent);
+ mParent.layout(true);
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PlayAnimationMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PlayAnimationMenu.java
index 3fcae86..812ece4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PlayAnimationMenu.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PlayAnimationMenu.java
@@ -15,8 +15,8 @@
*/
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
-import static com.android.ide.eclipse.adt.AndroidConstants.WS_SEP;
-import static com.android.sdklib.SdkConstants.FD_ANIMATOR;
+import static com.android.AndroidConstants.FD_RES_ANIMATOR;
+import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
import static com.android.sdklib.SdkConstants.FD_RESOURCES;
import com.android.ide.common.rendering.api.Capability;
@@ -124,7 +124,8 @@ public class PlayAnimationMenu extends SubmenuAction {
new ActionContributionItem(sub).fill(menu, -1);
}
} else {
- addDisabledMessageItem("Not supported on platform");
+ addDisabledMessageItem(
+ "Not supported for this SDK version; try changing the Render Target");
}
}
@@ -228,7 +229,7 @@ public class PlayAnimationMenu extends SubmenuAction {
LayoutEditor editor = mCanvas.getLayoutEditor();
IWorkbenchWindow workbenchWindow = editor.getEditorSite().getWorkbenchWindow();
IWorkbench workbench = workbenchWindow.getWorkbench();
- String animationDir = FD_RESOURCES + WS_SEP + FD_ANIMATOR;
+ String animationDir = FD_RESOURCES + WS_SEP + FD_RES_ANIMATOR;
Pair<IProject, String> pair = Pair.of(editor.getProject(), animationDir);
IStructuredSelection selection = new StructuredSelection(pair);
wizard.init(workbench, selection);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java
index 1b48c7c..80cc87d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java
@@ -17,8 +17,8 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
-import static com.android.ide.eclipse.adt.AndroidConstants.DOT_PNG;
-import static com.android.ide.eclipse.adt.AndroidConstants.DOT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.DOT_PNG;
+import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
import com.android.ide.common.rendering.LayoutLibrary;
import com.android.ide.common.rendering.api.Capability;
@@ -33,6 +33,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
@@ -41,6 +42,7 @@ import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.util.Pair;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.RGB;
import org.w3c.dom.Attr;
@@ -236,7 +238,7 @@ public class PreviewIconFactory {
// Important to get these sizes large enough for clients that don't support
// RenderMode.FULL_EXPAND such as 1.6
int width = 200;
- int height = 2000;
+ int height = documentElement.getChildNodes().getLength() == 1 ? 400 : 1600;
Set<UiElementNode> expandNodes = Collections.<UiElementNode>emptySet();
RenderingMode renderingMode = RenderingMode.FULL_EXPAND;
@@ -309,6 +311,13 @@ public class PreviewIconFactory {
}
}
}
+ } else {
+ if (session.getResult().getException() != null) {
+ AdtPlugin.log(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ } else if (session.getResult().getErrorMessage() != null) {
+ AdtPlugin.log(IStatus.WARNING, session.getResult().getErrorMessage());
+ }
}
session.dispose();
@@ -329,7 +338,7 @@ public class PreviewIconFactory {
RGB background = null;
RGB foreground = null;
- ResourceResolver resources = mPalette.getEditor().createResolver();
+ ResourceResolver resources = mPalette.getEditor().getResourceResolver();
StyleResourceValue theme = resources.getCurrentTheme();
if (theme != null) {
background = resolveThemeColor(resources, "windowBackground"); //$NON-NLS-1$
@@ -488,6 +497,19 @@ public class PreviewIconFactory {
}
private String getFileName(ElementDescriptor descriptor) {
+ if (descriptor instanceof PaletteMetadataDescriptor) {
+ PaletteMetadataDescriptor pmd = (PaletteMetadataDescriptor) descriptor;
+ StringBuilder sb = new StringBuilder();
+ String name = pmd.getUiName();
+ // Strip out whitespace, parentheses, etc.
+ for (int i = 0, n = name.length(); i < n; i++) {
+ char c = name.charAt(i);
+ if (Character.isLetter(c)) {
+ sb.append(c);
+ }
+ }
+ return sb.toString() + DOT_PNG;
+ }
return descriptor.getUiName() + DOT_PNG;
}
@@ -539,7 +561,7 @@ public class PreviewIconFactory {
if (themeName.startsWith(themeNamePrefix)) {
themeName = themeName.substring(themeNamePrefix.length());
}
- String dirName = String.format("palette-preview-r10-%s-%s-%s", cleanup(targetName),
+ String dirName = String.format("palette-preview-r11c-%s-%s-%s", cleanup(targetName),
cleanup(themeName), cleanup(mPalette.getCurrentDevice()));
IPath dirPath = pluginState.append(dirName);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java
index d33fffc..daf9655 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java
@@ -24,7 +24,6 @@ import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
@@ -98,39 +97,18 @@ public class PropertySheetPage extends org.eclipse.ui.views.properties.PropertyS
// Fix the selection background. In Eclipse 3.5 and 3.6, the selection color
// is white, painted on top of a white or light blue background (table striping),
// which is practically unreadable. This is fixed in 3.7M3, but we need a workaround
- // for earlier releases. The following code performs custom painting of this region,
- // and is based on the snippet "draw a custom gradient selection for tree" (snippet 226)
- // from http://www.eclipse.org/swt/snippets/ .
+ // for earlier releases. This just paints a solid color under the current line in
+ // the left column.
tree.addListener(SWT.EraseItem, new Listener() {
public void handleEvent(Event event) {
- if ((event.detail & SWT.SELECTED) != 0) {
+ if ((event.detail & SWT.SELECTED) != 0 && event.index == 0) {
GC gc = event.gc;
- Rectangle area = tree.getClientArea();
- int columnCount = tree.getColumnCount();
- if (event.index == columnCount - 1 || columnCount == 0) {
- int width = area.x + area.width - event.x;
- if (width > 0) {
- Region region = new Region();
- gc.getClipping(region);
- region.add(event.x, event.y, width, event.height);
- gc.setClipping(region);
- region.dispose();
- }
- }
- gc.setAdvanced(true);
- if (gc.getAdvanced()) {
- gc.setAlpha(127);
- }
Rectangle rect = event.getBounds();
- Color foreground = gc.getForeground();
Color background = gc.getBackground();
Display display = tree.getDisplay();
- gc.setForeground(display.getSystemColor(SWT.COLOR_LIST_SELECTION));
- gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
- gc.fillGradientRectangle(0, rect.y, 500, rect.height, false);
- gc.setForeground(foreground);
+ gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION));
+ gc.fillRectangle(rect.x, rect.y, rect.width, rect.height);
gc.setBackground(background);
- event.detail &= ~SWT.SELECTED;
}
}
});
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
index 983bcf5..01b9314 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
@@ -31,7 +31,7 @@ import java.util.List;
/**
* Represents one selection in {@link LayoutCanvas}.
*/
-/* package */ class SelectionItem {
+class SelectionItem {
/** Current selected view info. Can be null. */
private final CanvasViewInfo mCanvasViewInfo;
@@ -139,4 +139,18 @@ import java.util.List;
return elements.toArray(new SimpleElement[elements.size()]);
}
+
+ /**
+ * Returns true if this selection item is a layout
+ *
+ * @return true if this selection item is a layout
+ */
+ public boolean isLayout() {
+ UiViewElementNode node = mCanvasViewInfo.getUiViewNode();
+ if (node != null) {
+ return node.getDescriptor().hasChildren();
+ } else {
+ return false;
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
index 1aad0f2..c5a840b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
@@ -16,11 +16,17 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import com.android.ide.common.api.INode;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.sdklib.SdkConstants;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.gef.ui.parts.TreeViewer;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.Separator;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
@@ -30,8 +36,10 @@ import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IWorkbenchPartSite;
import org.w3c.dom.Node;
@@ -107,7 +115,7 @@ public class SelectionManager implements ISelectionProvider {
* @see #getSelection() {@link #getSelection()} to retrieve a {@link TreeViewer}
* compatible {@link ISelection}.
*/
- /* package */ List<SelectionItem> getSelections() {
+ List<SelectionItem> getSelections() {
return mUnmodifiableSelection;
}
@@ -135,12 +143,7 @@ public class SelectionManager implements ISelectionProvider {
for (SelectionItem cs : mSelections) {
CanvasViewInfo vi = cs.getViewInfo();
if (vi != null) {
- ArrayList<Object> segments = new ArrayList<Object>();
- while (vi != null) {
- segments.add(0, vi);
- vi = vi.getParent();
- }
- paths.add(new TreePath(segments.toArray()));
+ paths.add(getTreePath(vi));
}
}
@@ -148,6 +151,22 @@ public class SelectionManager implements ISelectionProvider {
}
/**
+ * Create a {@link TreePath} from the given view info
+ *
+ * @param viewInfo the view info to look up a tree path for
+ * @return a {@link TreePath} for the given view info
+ */
+ public static TreePath getTreePath(CanvasViewInfo viewInfo) {
+ ArrayList<Object> segments = new ArrayList<Object>();
+ while (viewInfo != null) {
+ segments.add(0, viewInfo);
+ viewInfo = viewInfo.getParent();
+ }
+
+ return new TreePath(segments.toArray());
+ }
+
+ /**
* Sets the selection. It must be an {@link ITreeSelection} where each segment
* of the tree path is a {@link CanvasViewInfo}. A null selection is considered
* as an empty selection.
@@ -177,6 +196,7 @@ public class SelectionManager implements ISelectionProvider {
if (!mSelections.isEmpty()) {
mSelections.clear();
mAltSelection = null;
+ updateActionsFromSelection();
redraw();
}
return;
@@ -210,7 +230,7 @@ public class SelectionManager implements ISelectionProvider {
mSelections.add(createSelection(newVi));
changed = true;
}
- if (newVi.isInvisibleParent()) {
+ if (newVi.isInvisible()) {
redoLayout = true;
}
}
@@ -230,7 +250,7 @@ public class SelectionManager implements ISelectionProvider {
}
if (changed) {
redraw();
- updateMenuActions();
+ updateActionsFromSelection();
}
}
@@ -240,6 +260,34 @@ public class SelectionManager implements ISelectionProvider {
}
/**
+ * The menu has been activated; ensure that the menu click is over the existing
+ * selection, and if not, update the selection.
+ *
+ * @param e the {@link MenuDetectEvent} which triggered the menu
+ */
+ public void menuClick(MenuDetectEvent e) {
+ LayoutPoint p = ControlPoint.create(mCanvas, e).toLayout();
+
+ // Right click button is used to display a context menu.
+ // If there's an existing selection and the click is anywhere in this selection
+ // and there are no modifiers being used, we don't want to change the selection.
+ // Otherwise we select the item under the cursor.
+
+ for (SelectionItem cs : mSelections) {
+ if (cs.isRoot()) {
+ continue;
+ }
+ if (cs.getRect().contains(p.x, p.y)) {
+ // The cursor is inside the selection. Don't change anything.
+ return;
+ }
+ }
+
+ CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p);
+ selectSingle(vi);
+ }
+
+ /**
* Performs selection for a mouse event.
* <p/>
* Shift key (or Command on the Mac) is used to toggle in multi-selection.
@@ -383,7 +431,7 @@ public class SelectionManager implements ISelectionProvider {
if (vi != null) {
mSelections.add(createSelection(vi));
- if (vi.isInvisibleParent()) {
+ if (vi.isInvisible()) {
redoLayout = true;
}
}
@@ -425,7 +473,7 @@ public class SelectionManager implements ISelectionProvider {
if (viewInfos != null) {
for (CanvasViewInfo viewInfo : viewInfos) {
mSelections.add(createSelection(viewInfo));
- if (viewInfo.isInvisibleParent()) {
+ if (viewInfo.isInvisible()) {
redoLayout = true;
}
}
@@ -440,11 +488,28 @@ public class SelectionManager implements ISelectionProvider {
redraw();
}
+ public void select(Collection<INode> nodes) {
+ List<CanvasViewInfo> infos = new ArrayList<CanvasViewInfo>(nodes.size());
+ for (INode node : nodes) {
+ CanvasViewInfo info = mCanvas.getViewHierarchy().findViewInfoFor(node);
+ if (info != null) {
+ infos.add(info);
+ }
+ }
+ selectMultiple(infos);
+ }
+
/**
* Selects the visual element corresponding to the given XML node
* @param xmlNode The Node whose element we want to select.
*/
/* package */ void select(Node xmlNode) {
+ if (xmlNode == null) {
+ return;
+ } else if (xmlNode.getNodeType() == Node.TEXT_NODE) {
+ xmlNode = xmlNode.getParentNode();
+ }
+
CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoFor(xmlNode);
if (vi != null && !vi.isRoot()) {
selectSingle(vi);
@@ -504,11 +569,72 @@ public class SelectionManager implements ISelectionProvider {
mSelections.add(createSelection(vi));
}
+ fireSelectionChanged();
+ redraw();
+ }
+ public void selectNone() {
+ mSelections.clear();
+ mAltSelection = null;
fireSelectionChanged();
redraw();
}
+ /** Selects the parent of the current selection */
+ public void selectParent() {
+ if (mSelections.size() == 1) {
+ CanvasViewInfo parent = mSelections.get(0).getViewInfo().getParent();
+ if (parent != null) {
+ selectSingle(parent);
+ }
+ }
+ }
+
+ /** Finds all widgets in the layout that have the same type as the primary */
+ public void selectSameType() {
+ // Find all
+ if (mSelections.size() == 1) {
+ CanvasViewInfo viewInfo = mSelections.get(0).getViewInfo();
+ ElementDescriptor descriptor = viewInfo.getUiViewNode().getDescriptor();
+ mSelections.clear();
+ mAltSelection = null;
+ addSameType(mCanvas.getViewHierarchy().getRoot(), descriptor);
+ fireSelectionChanged();
+ redraw();
+ }
+ }
+
+ /** Helper for {@link #selectSameType} */
+ private void addSameType(CanvasViewInfo root, ElementDescriptor descriptor) {
+ if (root.getUiViewNode().getDescriptor() == descriptor) {
+ mSelections.add(createSelection(root));
+ }
+
+ for (CanvasViewInfo child : root.getChildren()) {
+ addSameType(child, descriptor);
+ }
+ }
+
+ /** Selects the siblings of the primary */
+ public void selectSiblings() {
+ // Find all
+ if (mSelections.size() == 1) {
+ CanvasViewInfo vi = mSelections.get(0).getViewInfo();
+ mSelections.clear();
+ mAltSelection = null;
+ CanvasViewInfo parent = vi.getParent();
+ if (parent == null) {
+ selectNone();
+ } else {
+ for (CanvasViewInfo child : parent.getChildren()) {
+ mSelections.add(createSelection(child));
+ }
+ fireSelectionChanged();
+ redraw();
+ }
+ }
+ }
+
/**
* Returns true if and only if there is currently more than one selected
* item.
@@ -601,13 +727,25 @@ public class SelectionManager implements ISelectionProvider {
}
});
+ updateActionsFromSelection();
+ } finally {
+ mInsideUpdateSelection = false;
+ }
+ }
+
+ /**
+ * Updates menu actions and the layout action bar after a selection change - these are
+ * actions that depend on the selection
+ */
+ private void updateActionsFromSelection() {
+ LayoutEditor editor = mCanvas.getLayoutEditor();
+ if (editor != null) {
// Update menu actions that depend on the selection
updateMenuActions();
// Update the layout actions bar
- mCanvas.getLayoutEditor().getGraphicalEditor().getLayoutActionBar().updateSelection();
- } finally {
- mInsideUpdateSelection = false;
+ LayoutActionBar layoutActionBar = editor.getGraphicalEditor().getLayoutActionBar();
+ layoutActionBar.updateSelection();
}
}
@@ -685,21 +823,28 @@ public class SelectionManager implements ISelectionProvider {
* Update the outline selection to select the given nodes, asynchronously.
* @param nodes The nodes to be selected
*/
- public void updateOutlineSelection(final List<INode> nodes) {
+ public void setOutlineSelection(final List<INode> nodes) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
selectDropped(nodes);
- OutlinePage outlinePage = mCanvas.getOutlinePage();
- IWorkbenchPartSite site = outlinePage.getEditor().getSite();
- ISelectionProvider selectionProvider = site.getSelectionProvider();
- ISelection selection = selectionProvider.getSelection();
- if (selection != null) {
- outlinePage.setSelection(selection);
- }
+ syncOutlineSelection();
}
});
}
+ /**
+ * Syncs the current selection to the outline, synchronously.
+ */
+ public void syncOutlineSelection() {
+ OutlinePage outlinePage = mCanvas.getOutlinePage();
+ IWorkbenchPartSite site = outlinePage.getEditor().getSite();
+ ISelectionProvider selectionProvider = site.getSelectionProvider();
+ ISelection selection = selectionProvider.getSelection();
+ if (selection != null) {
+ outlinePage.setSelection(selection);
+ }
+ }
+
private void updateMenuActions() {
boolean hasSelection = !mSelections.isEmpty();
mCanvas.updateMenuActionState(hasSelection);
@@ -713,4 +858,111 @@ public class SelectionManager implements ISelectionProvider {
return new SelectionItem(vi, mCanvas.getRulesEngine(),
mCanvas.getNodeFactory());
}
+
+ /**
+ * Returns true if there is nothing selected
+ *
+ * @return true if there is nothing selected
+ */
+ public boolean isEmpty() {
+ return mSelections.size() == 0;
+ }
+
+ /**
+ * "Select" context menu which lists various menu options related to selection:
+ * <ul>
+ * <li> Select All
+ * <li> Select Parent
+ * <li> Select None
+ * <li> Select Siblings
+ * <li> Select Same Type
+ * </ul>
+ * etc.
+ */
+ public static class SelectionMenu extends SubmenuAction {
+ private final GraphicalEditorPart mEditor;
+
+ public SelectionMenu(GraphicalEditorPart editor) {
+ super("Select");
+ mEditor = editor;
+ }
+
+ @Override
+ public String getId() {
+ return "-selectionmenu"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected void addMenuItems(Menu menu) {
+ LayoutCanvas canvas = mEditor.getCanvasControl();
+ SelectionManager selectionManager = canvas.getSelectionManager();
+ List<SelectionItem> selections = selectionManager.getSelections();
+ boolean selectedOne = selections.size() == 1;
+ boolean notRoot = selectedOne && !selections.get(0).isRoot();
+ boolean haveSelection = selections.size() > 0;
+
+ Action a;
+ a = selectionManager.new SelectAction("Select Parent", SELECT_PARENT);
+ new ActionContributionItem(a).fill(menu, -1);
+ a.setEnabled(notRoot);
+ a.setAccelerator(SWT.ESC);
+
+ a = selectionManager.new SelectAction("Select Siblings", SELECT_SIBLINGS);
+ new ActionContributionItem(a).fill(menu, -1);
+ a.setEnabled(notRoot);
+
+ a = selectionManager.new SelectAction("Select Same Type", SELECT_SAME_TYPE);
+ new ActionContributionItem(a).fill(menu, -1);
+ a.setEnabled(selectedOne);
+
+ new Separator().fill(menu, -1);
+
+ // Special case for Select All: Use global action
+ a = canvas.getSelectAllAction();
+ new ActionContributionItem(a).fill(menu, -1);
+ a.setEnabled(true);
+
+ a = selectionManager.new SelectAction("Select None", SELECT_NONE);
+ new ActionContributionItem(a).fill(menu, -1);
+ a.setEnabled(haveSelection);
+ }
+ }
+
+ private static final int SELECT_PARENT = 1;
+ private static final int SELECT_SIBLINGS = 2;
+ private static final int SELECT_SAME_TYPE = 3;
+ private static final int SELECT_NONE = 4; // SELECT_ALL is handled separately
+
+ private class SelectAction extends Action {
+ private final int mType;
+
+ public SelectAction(String title, int type) {
+ super(title, IAction.AS_PUSH_BUTTON);
+ mType = type;
+ }
+
+ @Override
+ public void run() {
+ switch (mType) {
+ case SELECT_NONE:
+ selectNone();
+ break;
+ case SELECT_PARENT:
+ selectParent();
+ break;
+ case SELECT_SAME_TYPE:
+ selectSameType();
+ break;
+ case SELECT_SIBLINGS:
+ selectSiblings();
+ break;
+ }
+
+ List<INode> nodes = new ArrayList<INode>();
+ for (SelectionItem item : getSelections()) {
+ nodes.add(item.getNode());
+ }
+ setOutlineSelection(nodes);
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java
index 2748297..2a926a7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java
@@ -44,7 +44,12 @@ public enum SwtDrawingStyle {
/**
* The style definition corresponding to {@link DrawingStyle#HOVER}
*/
- HOVER(null, 0, new RGB(0xFF, 0xFF, 0xFF), 64, 1, SWT.LINE_DOT),
+ HOVER(null, 0, new RGB(0xFF, 0xFF, 0xFF), 40, 1, SWT.LINE_DOT),
+
+ /**
+ * The style definition corresponding to {@link DrawingStyle#HOVER}
+ */
+ HOVER_SELECTION(null, 0, new RGB(0xFF, 0xFF, 0xFF), 10, 1, SWT.LINE_DOT),
/**
* The style definition corresponding to {@link DrawingStyle#ANCHOR}
@@ -59,7 +64,7 @@ public enum SwtDrawingStyle {
/**
* The style definition corresponding to {@link DrawingStyle#DROP_RECIPIENT}
*/
- DROP_RECIPIENT(new RGB(0xFF, 0x99, 0x00), 255, new RGB(0xFF, 0x99, 0x00), 160, 1,
+ DROP_RECIPIENT(new RGB(0xFF, 0x99, 0x00), 255, new RGB(0xFF, 0x99, 0x00), 160, 2,
SWT.LINE_SOLID),
/**
@@ -199,6 +204,8 @@ public enum SwtDrawingStyle {
return GUIDELINE;
case HOVER:
return HOVER;
+ case HOVER_SELECTION:
+ return HOVER_SELECTION;
case ANCHOR:
return ANCHOR;
case OUTLINE:
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
index eac2228..10625bb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
@@ -44,6 +44,8 @@ import java.util.Set;
* operations on this set.
*/
public class ViewHierarchy {
+ private static final boolean DUMP_INFO = false;
+
private LayoutCanvas mCanvas;
/**
@@ -80,7 +82,7 @@ public class ViewHierarchy {
private boolean mIsResultValid;
/**
- * A list of invisible parents (see {@link CanvasViewInfo#isInvisibleParent()} for
+ * A list of invisible parents (see {@link CanvasViewInfo#isInvisible()} for
* details) in the current view hierarchy.
*/
private final List<CanvasViewInfo> mInvisibleParents = new ArrayList<CanvasViewInfo>();
@@ -134,7 +136,8 @@ public class ViewHierarchy {
* {@link LayoutCanvas#showInvisibleViews}) where individual invisible
* nodes are padded during certain interactions.
*/
- /* package */ void setSession(RenderSession session, Set<UiElementNode> explodedNodes) {
+ /* package */ void setSession(RenderSession session, Set<UiElementNode> explodedNodes,
+ boolean layoutlib5) {
// replace the previous scene, so the previous scene must be disposed.
if (mSession != null) {
mSession.dispose();
@@ -155,7 +158,7 @@ public class ViewHierarchy {
// via drag & drop, etc.
if (hasMergeRoot()) {
ViewInfo mergeRoot = createMergeInfo(session);
- infos = CanvasViewInfo.create(mergeRoot);
+ infos = CanvasViewInfo.create(mergeRoot, layoutlib5);
} else {
infos = null;
}
@@ -163,11 +166,15 @@ public class ViewHierarchy {
if (rootList.size() > 1 && hasMergeRoot()) {
ViewInfo mergeRoot = createMergeInfo(session);
mergeRoot.setChildren(rootList);
- infos = CanvasViewInfo.create(mergeRoot);
+ infos = CanvasViewInfo.create(mergeRoot, layoutlib5);
} else {
ViewInfo root = rootList.get(0);
+
if (root != null) {
- infos = CanvasViewInfo.create(root);
+ infos = CanvasViewInfo.create(root, layoutlib5);
+ if (DUMP_INFO) {
+ dump(root, 0);
+ }
} else {
infos = null;
}
@@ -176,12 +183,26 @@ public class ViewHierarchy {
if (infos != null) {
mLastValidViewInfoRoot = infos.getFirst();
mIncludedBounds = infos.getSecond();
+
+ if (mLastValidViewInfoRoot.getUiViewNode() == null &&
+ mLastValidViewInfoRoot.getChildren().isEmpty()) {
+ GraphicalEditorPart editor = mCanvas.getLayoutEditor().getGraphicalEditor();
+ if (editor.getIncludedWithin() != null) {
+ // Somehow, this view was supposed to be rendered within another
+ // view, yet this view was rendered as part of the other view.
+ // In that case, abort attempting to show included in; clear the
+ // include context and trigger a standalone re-render.
+ editor.showIn(null);
+ return;
+ }
+ }
+
} else {
mLastValidViewInfoRoot = null;
mIncludedBounds = null;
}
- updateNodeProxies(mLastValidViewInfoRoot, null);
+ updateNodeProxies(mLastValidViewInfoRoot);
// Update the data structures related to tracking invisible and exploded nodes.
// We need to find the {@link CanvasViewInfo} objects that correspond to
@@ -238,7 +259,7 @@ public class ViewHierarchy {
* This is a recursive call that updates the whole hierarchy starting at the given
* view info.
*/
- private void updateNodeProxies(CanvasViewInfo vi, UiViewElementNode parentKey) {
+ private void updateNodeProxies(CanvasViewInfo vi) {
if (vi == null) {
return;
}
@@ -250,7 +271,7 @@ public class ViewHierarchy {
}
for (CanvasViewInfo child : vi.getChildren()) {
- updateNodeProxies(child, key);
+ updateNodeProxies(child);
}
}
@@ -274,7 +295,7 @@ public class ViewHierarchy {
return;
}
- if (vi.isInvisibleParent()) {
+ if (vi.isInvisible()) {
mInvisibleParents.add(vi);
} else if (invisibleNodes != null) {
UiViewElementNode key = vi.getUiViewNode();
@@ -670,4 +691,44 @@ public class ViewHierarchy {
return mIncludedBounds;
}
+ /**
+ * Dumps a {@link ViewInfo} hierarchy to stdout
+ *
+ * @param info the {@link ViewInfo} object to dump
+ * @param depth the depth to indent it to
+ */
+ public static void dump(ViewInfo info, int depth) {
+ if (DUMP_INFO) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < depth; i++) {
+ sb.append(" "); //$NON-NLS-1$
+ }
+ sb.append(info.getClassName());
+ sb.append(" ["); //$NON-NLS-1$
+ sb.append(info.getLeft());
+ sb.append(","); //$NON-NLS-1$
+ sb.append(info.getTop());
+ sb.append(","); //$NON-NLS-1$
+ sb.append(info.getRight());
+ sb.append(","); //$NON-NLS-1$
+ sb.append(info.getBottom());
+ sb.append("]"); //$NON-NLS-1$
+ Object cookie = info.getCookie();
+ if (cookie instanceof UiViewElementNode) {
+ sb.append(" "); //$NON-NLS-1$
+ UiViewElementNode node = (UiViewElementNode) cookie;
+ sb.append("<"); //$NON-NLS-1$
+ sb.append(node.getDescriptor().getXmlName());
+ sb.append(">"); //$NON-NLS-1$
+ } else if (cookie != null) {
+ sb.append(" " + cookie); //$NON-NLS-1$
+ }
+
+ System.out.println(sb.toString());
+
+ for (ViewInfo child : info.getChildren()) {
+ dump(child, depth + 1);
+ }
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
index 59c6e15..bd8e75e 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
@@ -203,6 +203,12 @@ public class NodeProxy implements INode {
return insertOrAppend(viewFqcn, index);
}
+ public void removeChild(INode node) {
+ checkEditOK();
+
+ ((NodeProxy) node).mNode.deleteXmlNode();
+ }
+
private INode insertOrAppend(String viewFqcn, int index) {
checkEditOK();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java
new file mode 100644
index 0000000..3380a38
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.gre;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleAttribute;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleElement;
+
+import org.eclipse.swt.graphics.Image;
+import org.w3c.dom.Element;
+
+/**
+ * Special version of {@link ViewElementDescriptor} which is initialized by the palette
+ * with specific metadata for how to instantiate particular variations of an existing
+ * {@link ViewElementDescriptor} with initial values.
+ */
+public class PaletteMetadataDescriptor extends ViewElementDescriptor {
+ private String mInitString;
+ private String mIconName;
+
+ public PaletteMetadataDescriptor(ViewElementDescriptor descriptor, String displayName,
+ String initString, String iconName) {
+ super(descriptor.getXmlName(),
+ displayName,
+ descriptor.getFullClassName(),
+ descriptor.getTooltip(),
+ descriptor.getSdkUrl(),
+ descriptor.getAttributes(),
+ descriptor.getLayoutAttributes(),
+ descriptor.getChildren(), descriptor.getMandatory() == Mandatory.MANDATORY);
+ mInitString = initString;
+ mIconName = iconName;
+ }
+
+ /**
+ * Returns a String which contains a comma separated list of name=value tokens,
+ * where the name can start with "android:" to indicate a property in the android namespace,
+ * or no prefix for plain attributes.
+ *
+ * @return the initialization string, which can be empty but never null
+ */
+ public String getInitializedAttributes() {
+ return mInitString != null ? mInitString : ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public Image getGenericIcon() {
+ if (mIconName != null) {
+ IconFactory factory = IconFactory.getInstance();
+ Image icon = factory.getIcon(mIconName);
+ if (icon != null) {
+ return icon;
+ }
+ }
+
+ return super.getGenericIcon();
+ }
+
+ /**
+ * Initializes a new {@link SimpleElement} with the palette initialization
+ * configuration
+ *
+ * @param element the new element to initialize
+ */
+ public void initializeNew(SimpleElement element) {
+ initializeNew(element, null);
+ }
+
+ /**
+ * Initializes a new {@link Element} with the palette initialization configuration
+ *
+ * @param element the new element to initialize
+ */
+ public void initializeNew(Element element) {
+ initializeNew(null, element);
+ }
+
+ private void initializeNew(SimpleElement simpleElement, Element domElement) {
+ String initializedAttributes = mInitString;
+ if (initializedAttributes != null && initializedAttributes.length() > 0) {
+ for (String s : initializedAttributes.split(",")) { //$NON-NLS-1$
+ String[] nameValue = s.split("="); //$NON-NLS-1$
+ String name = nameValue[0];
+ String value = nameValue[1];
+ String nameSpace = ""; //$NON-NLS-1$
+ if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
+ name = name.substring(ANDROID_NS_NAME_PREFIX.length());
+ nameSpace = ANDROID_URI;
+ }
+
+ if (simpleElement != null) {
+ SimpleAttribute attr = new SimpleAttribute(nameSpace, name, value);
+ simpleElement.addAttribute(attr);
+ }
+
+ if (domElement != null) {
+ domElement.setAttributeNS(nameSpace, name, value);
+ }
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
index cde4e28..6838432 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gre;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE;
import com.android.ide.common.api.DropFeedback;
@@ -30,14 +31,17 @@ import com.android.ide.common.api.InsertType;
import com.android.ide.common.api.MenuAction;
import com.android.ide.common.api.Point;
import com.android.ide.common.layout.ViewRule;
+import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionManager;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleElement;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
+import com.android.ide.eclipse.adt.internal.resources.CyclicDependencyValidator;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
@@ -62,6 +66,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -600,7 +605,7 @@ public class RulesEngine {
// Deal with unknown descriptors; these lack the full qualified path and
// elements in the layout without a package are taken to be in the
// android.widget package.
- fqcn = "android.widget." + fqcn; //$NON-NLS-1$
+ fqcn = ANDROID_WIDGET_PREFIX + fqcn;
}
// Try to find a rule matching the "real" FQCN. If we find it, we're done.
@@ -851,7 +856,7 @@ public class RulesEngine {
IProject project = editor.getProject();
if (project != null) {
// get the resource repository for this project and the system resources.
- IResourceRepository projectRepository =
+ ResourceRepository projectRepository =
ResourceManager.getInstance().getProjectResources(project);
Shell shell = AdtPlugin.getDisplay().getActiveShell();
if (shell == null) {
@@ -873,12 +878,17 @@ public class RulesEngine {
}
public String displayResourceInput(String resourceTypeName, String currentValue) {
+ return displayResourceInput(resourceTypeName, currentValue, null);
+ }
+
+ private String displayResourceInput(String resourceTypeName, String currentValue,
+ IInputValidator validator) {
AndroidXmlEditor editor = mEditor.getLayoutEditor();
IProject project = editor.getProject();
- ResourceType type = ResourceType.valueOf(resourceTypeName.toUpperCase());
+ ResourceType type = ResourceType.getEnum(resourceTypeName);
if (project != null) {
// get the resource repository for this project and the system resources.
- IResourceRepository projectRepository = ResourceManager.getInstance()
+ ResourceRepository projectRepository = ResourceManager.getInstance()
.getProjectResources(project);
Shell shell = AdtPlugin.getDisplay().getActiveShell();
if (shell == null) {
@@ -886,15 +896,24 @@ public class RulesEngine {
}
AndroidTargetData data = editor.getTargetData();
- IResourceRepository systemRepository = data.getSystemResources();
+ ResourceRepository systemRepository = data.getFrameworkResources();
// open a resource chooser dialog for specified resource type.
ResourceChooser dlg = new ResourceChooser(project, type, projectRepository,
systemRepository, shell);
+ if (validator != null) {
+ // Ensure wide enough to accommodate validator error message
+ dlg.setSize(70, 10);
+ dlg.setInputValidator(validator);
+ }
+
dlg.setCurrentResource(currentValue);
- if (dlg.open() == Window.OK) {
+ int result = dlg.open();
+ if (result == ResourceChooser.CLEAR_RETURN_CODE) {
+ return ""; //$NON-NLS-1$
+ } else if (result == Window.OK) {
return dlg.getCurrentResource();
}
}
@@ -921,5 +940,25 @@ public class RulesEngine {
return null;
}
+
+ public String displayIncludeSourceInput() {
+ AndroidXmlEditor editor = mEditor.getLayoutEditor();
+ IInputValidator validator = CyclicDependencyValidator.create(editor.getInputFile());
+ return displayResourceInput(ResourceType.LAYOUT.getName(), null, validator);
+ }
+
+ public void select(final Collection<INode> nodes) {
+ LayoutCanvas layoutCanvas = mEditor.getCanvasControl();
+ final SelectionManager selectionManager = layoutCanvas.getSelectionManager();
+ selectionManager.select(nodes);
+ // ALSO run an async select since immediately after nodes are created they
+ // may not be selectable. We can't ONLY run an async exec since
+ // code may depend on operating on the selection.
+ layoutCanvas.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ selectionManager.select(nodes);
+ }
+ });
+ }
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepository.java
index 589ad2d..cf6acba 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepository.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepository.java
@@ -16,14 +16,15 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gre;
-import static com.android.ide.common.api.IViewMetadata.FillPreference.NONE;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
+import com.android.annotations.VisibleForTesting;
import com.android.ide.common.api.IViewMetadata.FillPreference;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
@@ -38,13 +39,14 @@ import org.xml.sax.InputSource;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.TreeMap;
+import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -147,9 +149,7 @@ public class ViewMetadataRepository {
// Therefore, we instead use the convention that the id is the fully qualified
// class name, with .'s replaced with _'s.
-
// Special case: for tab host we aren't allowed to mess with the id
-
String id = element.getAttributeNS(ANDROID_URI, ATTR_ID);
if ("@android:id/tabhost".equals(id)) {
@@ -218,28 +218,11 @@ public class ViewMetadataRepository {
Node childNode = children.item(j);
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element) childNode;
- String fqcn = child.getAttribute("class"); //$NON-NLS-1$
- String fill = child.getAttribute("fill"); //$NON-NLS-1$
- FillPreference fillPreference = null;
- if (fill.length() > 0) {
- fillPreference = fillTypes.get(fill);
- }
- if (fillPreference == null) {
- fillPreference = NONE;
- }
- String skip = child.getAttribute("skip"); //$NON-NLS-1$
- RenderMode mode = RenderMode.NORMAL;
- String render = child.getAttribute("render"); //$NON-NLS-1$
- if (render.length() > 0) {
- mode = RenderMode.get(render);
- }
- ViewData view = new ViewData(category, fqcn, fillPreference,
- skip.length() == 0 ? false : Boolean.valueOf(skip),
- mode);
+ ViewData view = createViewData(fillTypes, child,
+ null, FillPreference.NONE, RenderMode.NORMAL);
category.addView(view);
}
}
-
mCategories.add(category);
}
}
@@ -252,6 +235,62 @@ public class ViewMetadataRepository {
return mCategories;
}
+ private ViewData createViewData(Map<String, FillPreference> fillTypes,
+ Element child, String defaultFqcn, FillPreference defaultFill,
+ RenderMode defaultRender) {
+ String fqcn = child.getAttribute("class"); //$NON-NLS-1$
+ if (fqcn.length() == 0) {
+ fqcn = defaultFqcn;
+ }
+ String fill = child.getAttribute("fill"); //$NON-NLS-1$
+ FillPreference fillPreference = null;
+ if (fill.length() > 0) {
+ fillPreference = fillTypes.get(fill);
+ }
+ if (fillPreference == null) {
+ fillPreference = defaultFill;
+ }
+ String skip = child.getAttribute("skip"); //$NON-NLS-1$
+ RenderMode renderMode = defaultRender;
+ String render = child.getAttribute("render"); //$NON-NLS-1$
+ if (render.length() > 0) {
+ renderMode = RenderMode.get(render);
+ }
+ String displayName = child.getAttribute("name"); //$NON-NLS-1$
+ if (displayName.length() == 0) {
+ displayName = null;
+ }
+ String relatedTo = child.getAttribute("relatedTo"); //$NON-NLS-1$
+ ViewData view = new ViewData(fqcn, displayName, fillPreference,
+ skip.length() == 0 ? false : Boolean.valueOf(skip),
+ renderMode, relatedTo);
+
+ String init = child.getAttribute("init"); //$NON-NLS-1$
+ String icon = child.getAttribute("icon"); //$NON-NLS-1$
+
+ view.setInitString(init);
+ if (icon.length() > 0) {
+ view.setIconName(icon);
+ }
+
+ // Nested variations?
+ if (child.hasChildNodes()) {
+ // Palette variations
+ NodeList childNodes = child.getChildNodes();
+ for (int k = 0, kl = childNodes.getLength(); k < kl; k++) {
+ Node variationNode = childNodes.item(k);
+ if (variationNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element variation = (Element) variationNode;
+ ViewData variationView = createViewData(fillTypes, variation,
+ fqcn, fillPreference, renderMode);
+ view.addVariation(variationView);
+ }
+ }
+ }
+
+ return view;
+ }
+
/**
* Computes the palette entries for the given {@link AndroidTargetData}, looking up the
* available node descriptors, categorizing and sorting them.
@@ -267,108 +306,97 @@ public class ViewMetadataRepository {
List<Pair<String, List<ViewElementDescriptor>>> result =
new ArrayList<Pair<String, List<ViewElementDescriptor>>>();
- final Map<String, ViewData> viewMap = getClassToView();
- Map<CategoryData, List<ViewElementDescriptor>> categories =
- new TreeMap<CategoryData, List<ViewElementDescriptor>>();
-
- // Locate the "Other" category
- CategoryData other = null;
- for (CategoryData category : getCategories()) {
- if (category.getViewCount() == 0) {
- other = category;
- break;
- }
- }
-
List<List<ViewElementDescriptor>> lists = new ArrayList<List<ViewElementDescriptor>>(2);
LayoutDescriptors layoutDescriptors = targetData.getLayoutDescriptors();
lists.add(layoutDescriptors.getViewDescriptors());
lists.add(layoutDescriptors.getLayoutDescriptors());
+ // First record map of FQCN to ViewElementDescriptor such that we can quickly
+ // determine if a particular palette entry is available
+ Map<String, ViewElementDescriptor> fqcnToDescriptor =
+ new HashMap<String, ViewElementDescriptor>();
for (List<ViewElementDescriptor> list : lists) {
for (ViewElementDescriptor view : list) {
- ViewData viewData = getClassToView().get(view.getFullClassName());
- CategoryData category = other;
- if (viewData != null) {
- if (viewData.getSkip()) {
- continue;
- }
- category = viewData.getCategory();
+ String fqcn = view.getFullClassName();
+ if (fqcn == null) {
+ // <view> and <merge> tags etc
+ fqcn = view.getUiName();
}
+ fqcnToDescriptor.put(fqcn, view);
+ }
+ }
- List<ViewElementDescriptor> viewList = categories.get(category);
- if (viewList == null) {
- viewList = new ArrayList<ViewElementDescriptor>();
- categories.put(category, viewList);
- }
- viewList.add(view);
+ Set<ViewElementDescriptor> remaining = new HashSet<ViewElementDescriptor>(
+ layoutDescriptors.getViewDescriptors().size()
+ + layoutDescriptors.getLayoutDescriptors().size());
+ remaining.addAll(layoutDescriptors.getViewDescriptors());
+ remaining.addAll(layoutDescriptors.getLayoutDescriptors());
+ // Now iterate in palette metadata order over the items in the palette and include
+ // any that also appear as a descriptor
+ List<ViewElementDescriptor> categoryItems = new ArrayList<ViewElementDescriptor>();
+ for (CategoryData category : getCategories()) {
+ if (createCategories) {
+ categoryItems = new ArrayList<ViewElementDescriptor>();
+ }
+ for (ViewData view : category) {
+ String fqcn = view.getFcqn();
+ ViewElementDescriptor descriptor = fqcnToDescriptor.get(fqcn);
+ if (descriptor != null) {
+ if (view.getDisplayName() != null || view.getInitString().length() > 0) {
+ categoryItems.add(new PaletteMetadataDescriptor(descriptor,
+ view.getDisplayName(), view.getInitString(), view.getIconName()));
+ } else {
+ categoryItems.add(descriptor);
+ }
+ remaining.remove(descriptor);
+
+ if (view.hasVariations()) {
+ for (ViewData variation : view.getVariations()) {
+ String init = variation.getInitString();
+ String icon = variation.getIconName();
+ ViewElementDescriptor desc = new PaletteMetadataDescriptor(descriptor,
+ variation.getDisplayName(), init, icon);
+ categoryItems.add(desc);
+ }
+ }
+ }
}
- }
- if (!createCategories) {
- // Squash all categories into a single one, "Views"
- Map<CategoryData, List<ViewElementDescriptor>> singleCategory =
- new HashMap<CategoryData, List<ViewElementDescriptor>>();
- List<ViewElementDescriptor> items = new ArrayList<ViewElementDescriptor>(100);
- for (Map.Entry<CategoryData, List<ViewElementDescriptor>> entry : categories.entrySet()) {
- items.addAll(entry.getValue());
+ if (createCategories && categoryItems.size() > 0) {
+ if (alphabetical) {
+ Collections.sort(categoryItems);
+ }
+ result.add(Pair.of(category.getName(), categoryItems));
}
- singleCategory.put(new CategoryData("Views"), items);
- categories = singleCategory;
}
- for (Map.Entry<CategoryData, List<ViewElementDescriptor>> entry : categories.entrySet()) {
- String name = entry.getKey().getName();
- List<ViewElementDescriptor> items = entry.getValue();
- if (items == null) {
- continue; // empty category
+ if (remaining.size() > 0) {
+ List<ViewElementDescriptor> otherItems = new ArrayList<ViewElementDescriptor>(remaining);
+ // Always sorted, we don't have a natural order for these unknowns
+ Collections.sort(otherItems);
+ if (createCategories) {
+ result.add(Pair.of("Other", otherItems));
+ } else {
+ categoryItems.addAll(otherItems);
}
+ }
- // Natural sort of the descriptors
+ if (!createCategories) {
if (alphabetical) {
- Collections.sort(items);
- } else {
- Collections.sort(items, new Comparator<ViewElementDescriptor>() {
- public int compare(ViewElementDescriptor v1, ViewElementDescriptor v2) {
- String fqcn1 = v1.getFullClassName();
- String fqcn2 = v2.getFullClassName();
- if (fqcn1 == null) {
- // <view> and <merge> tags etc
- fqcn1 = v1.getUiName();
- }
- if (fqcn2 == null) {
- fqcn2 = v2.getUiName();
- }
- ViewData d1 = viewMap.get(fqcn1);
- ViewData d2 = viewMap.get(fqcn2);
-
- // Use natural sorting order of the view data
- // Sort unknown views to the end (and alphabetically among themselves)
- if (d1 != null) {
- if (d2 != null) {
- return d1.getOrdinal() - d2.getOrdinal();
- } else {
- return 1;
- }
- } else {
- if (d2 == null) {
- return v1.getUiName().compareTo(v2.getUiName());
- } else {
- return -1;
- }
- }
- }
- });
+ Collections.sort(categoryItems);
}
-
-
- result.add(Pair.of(name, items));
+ result.add(Pair.of("Views", categoryItems));
}
return result;
}
+ @VisibleForTesting
+ Collection<String> getAllFqcns() {
+ return getClassToView().keySet();
+ }
+
/**
* Metadata holder for a particular category - contains the name of the category, its
* ordinal (for natural/logical sorting order) and views contained in the category
@@ -396,10 +424,6 @@ public class ViewMetadataRepository {
return mName;
}
- public int getViewCount() {
- return mViews.size();
- }
-
// Implements Iterable<ViewData> such that we can use for-each on the category to
// enumerate its views
public Iterator<ViewData> iterator() {
@@ -418,29 +442,37 @@ public class ViewMetadataRepository {
private final String mFqcn;
/** Fill preference of the view */
private final FillPreference mFillPreference;
- /** The category that the view belongs to */
- private final CategoryData mCategory;
/** Skip this item in the palette? */
private final boolean mSkip;
/** Must this item be rendered alone? skipped? etc */
private final RenderMode mRenderMode;
+ /** Related views */
+ private final String mRelatedTo;
/** The relative rank of the view for natural ordering */
private final int mOrdinal = sNextOrdinal++;
+ /** List of optional variations */
+ private List<ViewData> mVariations;
+ /** Display name. Can be null. */
+ private String mDisplayName;
+ /**
+ * Optional initialization string - a comma separate set of name/value pairs to
+ * initialize the element with
+ */
+ private String mInitString;
+ /** The name of an icon (known to the {@link IconFactory} to show for this view */
+ private String mIconName;
/** Constructs a new view data for the given class */
- private ViewData(CategoryData category, String fqcn,
- FillPreference fillPreference, boolean skip, RenderMode renderMode) {
+ private ViewData(String fqcn, String displayName,
+ FillPreference fillPreference, boolean skip, RenderMode renderMode,
+ String relatedTo) {
super();
- mCategory = category;
mFqcn = fqcn;
+ mDisplayName = displayName;
mFillPreference = fillPreference;
mSkip = skip;
mRenderMode = renderMode;
- }
-
- /** Returns the category for views of this type */
- private CategoryData getCategory() {
- return mCategory;
+ mRelatedTo = relatedTo;
}
/** Returns the {@link FillPreference} for views of this type */
@@ -453,9 +485,8 @@ public class ViewMetadataRepository {
return mFqcn;
}
- /** Relative rank of this view type */
- private int getOrdinal() {
- return mOrdinal;
+ private String getDisplayName() {
+ return mDisplayName;
}
// Implements Comparable<ViewData> such that views can be sorted naturally
@@ -470,6 +501,64 @@ public class ViewMetadataRepository {
public boolean getSkip() {
return mSkip;
}
+
+ public List<String> getRelatedTo() {
+ if (mRelatedTo == null || mRelatedTo.length() == 0) {
+ return Collections.emptyList();
+ } else {
+ String[] basenames = mRelatedTo.split(","); //$NON-NLS-1$
+ List<String> result = new ArrayList<String>();
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+ Map<String, ViewData> classToView = repository.getClassToView();
+
+ List<String> fqns = new ArrayList<String>(classToView.keySet());
+ for (String basename : basenames) {
+ boolean found = false;
+ for (String fqcn : fqns) {
+ String suffix = '.' + basename;
+ if (fqcn.endsWith(suffix)) {
+ result.add(fqcn);
+ found = true;
+ break;
+ }
+ }
+ assert found : basename;
+ }
+
+ return result;
+ }
+ }
+
+ void addVariation(ViewData variation) {
+ if (mVariations == null) {
+ mVariations = new ArrayList<ViewData>(4);
+ }
+ mVariations.add(variation);
+ }
+
+ List<ViewData> getVariations() {
+ return mVariations;
+ }
+
+ boolean hasVariations() {
+ return mVariations != null && mVariations.size() > 0;
+ }
+
+ private void setInitString(String initString) {
+ this.mInitString = initString;
+ }
+
+ private String getInitString() {
+ return mInitString;
+ }
+
+ private void setIconName(String iconName) {
+ this.mIconName = iconName;
+ }
+
+ private String getIconName() {
+ return mIconName;
+ }
}
/**
@@ -501,7 +590,7 @@ public class ViewMetadataRepository {
return view.getRenderMode();
}
- return RenderMode.ALONE;
+ return RenderMode.NORMAL;
}
/**
@@ -520,6 +609,23 @@ public class ViewMetadataRepository {
return false;
}
+ /**
+ * Returns a set of fully qualified names for views that are closely related to the
+ * given view
+ *
+ * @param fqcn the fully qualified class name
+ * @return a list, never null but possibly empty, of views that are related to the
+ * view of the given type
+ */
+ public List<String> getRelatedTo(String fqcn) {
+ ViewData view = getClassToView().get(fqcn);
+ if (view != null) {
+ return view.getRelatedTo();
+ }
+
+ return Collections.emptyList();
+ }
+
/** Render mode for palette preview */
public enum RenderMode {
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml
index 627c2e9..442b76c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Palette Metadata
+ <!--
+ Palette Metadata
- This document provides additional designtime metadata for various Android views,
- such as logical palette categories (as well as a natural ordering of the views within
- their categories, fill-preferences (how a view will sets its width and height attributes
- when dropped into other views), and so on.
- -->
+ This document provides additional designtime metadata for various Android views, such as
+ logical palette categories (as well as a natural ordering of the views within their
+ categories, fill-preferences (how a view will sets its width and height attributes when
+ dropped into other views), and so on.
+ -->
<!DOCTYPE metadata [
<!--- The metadata consists of a series of category definitions -->
<!ELEMENT metadata (category)*>
@@ -15,10 +15,13 @@
<!ATTLIST category name CDATA #IMPLIED>
<!--- Each view is identified by its full class name and has various
other attributes such as a fill preference -->
-<!ELEMENT view EMPTY>
+<!ELEMENT view (view)*>
<!ATTLIST view
- class CDATA #REQUIRED
- previewXml CDATA #IMPLIED
+ class CDATA #IMPLIED
+ name CDATA #IMPLIED
+ init CDATA #IMPLIED
+ icon CDATA #IMPLIED
+ relatedTo CDATA #IMPLIED
skip (true|false) "false"
render (alone|skip|normal) "normal"
fill ( none|both|width|height|opposite|
@@ -26,81 +29,316 @@
>
]>
<metadata>
- <category name="Form Widgets">
- <view class="android.widget.TextView" />
- <view class="android.widget.Button" />
- <view class="android.widget.CheckBox" />
- <view class="android.widget.ToggleButton" />
- <view class="android.widget.RadioButton" />
- <view class="android.widget.CheckedTextView" />
- <view class="android.widget.Spinner" fill="width_in_vertical" />
- <view class="android.widget.EditText" fill="width_in_vertical" />
- <view class="android.widget.AutoCompleteTextView" fill="width_in_vertical" />
- <view class="android.widget.MultiAutoCompleteTextView" fill="width_in_vertical" />
- <view class="android.widget.ProgressBar" />
- <view class="android.widget.QuickContactBadge" />
- <view class="android.widget.RadioGroup" />
- <view class="android.widget.RatingBar" />
- <view class="android.widget.SeekBar" fill="width_in_vertical" />
+ <category
+ name="Form Widgets">
+ <view
+ class="android.widget.TextView"
+ name="TextView"
+ init=""
+ relatedTo="EditText,AutoCompleteTextView,MultiAutoCompleteTextView">
+ <view
+ name="Large Text"
+ init="android:textAppearance=?android:attr/textAppearanceLarge" />
+ <view
+ name="Medium Text"
+ init="android:textAppearance=?android:attr/textAppearanceMedium" />
+ <view
+ name="Small Text"
+ init="android:textAppearance=?android:attr/textAppearanceSmall" />
+ </view>
+ <view
+ class="android.widget.Button"
+ relatedTo="ImageButton" />
+ <view
+ class="android.widget.ToggleButton"
+ relatedTo="CheckBox" />
+ <view
+ class="android.widget.CheckBox"
+ relatedTo="RadioButton,ToggleButton,CheckedTextView" />
+ <view
+ class="android.widget.RadioButton"
+ relatedTo="CheckBox,ToggleButton" />
+ <view
+ class="android.widget.CheckedTextView"
+ relatedTo="TextView,CheckBox" />
+ <view
+ class="android.widget.Spinner"
+ relatedTo="EditText"
+ fill="width_in_vertical" />
+ <view
+ class="android.widget.ProgressBar"
+ relatedTo="SeekBar"
+ name="ProgressBar (Large)"
+ init="style=?android:attr/progressBarStyleLarge">
+ <view
+ name="ProgressBar (Normal)"
+ init="" />
+ <view
+ name="ProgressBar (Small)"
+ init="style=?android:attr/progressBarStyleSmall" />
+ <view
+ name="ProgressBar (Horizontal)"
+ init="style=?android:attr/progressBarStyleHorizontal" />
+ </view>
+ <view
+ class="android.widget.SeekBar"
+ relatedTo="ProgressBar"
+ fill="width_in_vertical" />
+ <view
+ class="android.widget.QuickContactBadge" />
+ <view
+ class="android.widget.RadioGroup" />
+ <view
+ class="android.widget.RatingBar" />
</category>
- <category name="Layouts">
- <view class="android.widget.LinearLayout" fill="opposite" render="skip"/>
- <view class="android.widget.RelativeLayout" fill="opposite" render="skip"/>
- <view class="android.widget.FrameLayout" fill="opposite" render="skip"/>
- <view class="android.widget.TableLayout" fill="opposite" render="skip"/>
- <view class="android.widget.TableRow" fill="opposite" render="skip"/>
+ <category
+ name="Text Fields">
+ <view
+ class="android.widget.EditText"
+ name="Plain Text"
+ init=""
+ relatedTo="Spinner,TextView,AutoCompleteTextView,MultiAutoCompleteTextView"
+ fill="width_in_vertical">
+ <view
+ name="Person Name"
+ init="android:inputType=textPersonName" />
+ <view
+ name="Password"
+ init="android:inputType=textPassword" />
+ <view
+ name="Password (Numeric)"
+ init="android:inputType=numberPassword" />
+ <view
+ name="E-mail"
+ init="android:inputType=textEmailAddress" />
+ <view
+ name="Phone"
+ init="android:inputType=phone" />
+ <view
+ name="Postal Address"
+ init="android:inputType=textPostalAddress" />
+ <view
+ name="Multiline Text"
+ init="android:inputType=textMultiLine" />
+ <view
+ name="Time"
+ init="android:inputType=time" />
+ <view
+ name="Date"
+ init="android:inputType=date" />
+ <view
+ name="Number"
+ init="android:inputType=number" />
+ <view
+ name="Number (Signed)"
+ init="android:inputType=numberSigned" />
+ <view
+ name="Number (Decimal)"
+ init="android:inputType=numberDecimal" />
+ </view>
+ <view
+ class="android.widget.AutoCompleteTextView"
+ fill="width_in_vertical" />
+ <view
+ class="android.widget.MultiAutoCompleteTextView"
+ fill="width_in_vertical" />
</category>
- <category name="Composite">
- <view class="android.widget.ListView" fill="width_in_vertical" render="skip"/>
- <view class="android.widget.ExpandableListView" fill="width_in_vertical" render="skip"/>
- <view class="android.widget.TwoLineListItem" render="skip"/>
- <view class="android.widget.GridView" fill="opposite" render="skip"/>
- <view class="android.widget.ScrollView" fill="opposite" render="skip"/>
- <view class="android.widget.HorizontalScrollView" render="skip"/>
- <view class="android.widget.SearchView" render="skip"/>
- <view class="android.widget.SlidingDrawer"/>
- <view class="android.widget.TabHost" fill="width_in_vertical" render="alone" />
- <view class="android.widget.TabWidget" render="alone" />
- <view class="android.webkit.WebView" fill="opposite" render="skip" />
+ <category
+ name="Layouts">
+ <view
+ class="android.widget.LinearLayout"
+ name="LinearLayout (Vertical)"
+ init="android:orientation=vertical"
+ icon="VerticalLinearLayout"
+ fill="opposite"
+ render="skip">
+ <view
+ name="LinearLayout (Horizontal)" />
+ </view>
+ <view
+ class="android.widget.RelativeLayout"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="android.widget.FrameLayout"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="include"
+ name="Include Other Layout"
+ render="skip" />
+ <view
+ class="android.widget.TableLayout"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="android.widget.TableRow"
+ fill="opposite"
+ render="skip" />
</category>
- <category name="Images &amp; Media">
- <view class="android.widget.ImageView" />
- <view class="android.widget.ImageButton" />
- <view class="android.widget.Gallery" fill="width_in_vertical" render="skip"/>
- <view class="android.widget.MediaController" render="skip"/>
- <view class="android.widget.VideoView" fill="opposite" render="skip"/>
+ <category
+ name="Composite">
+ <view
+ class="android.widget.ListView"
+ relatedTo="ExpandableListView"
+ fill="width_in_vertical" />
+ <view
+ class="android.widget.ExpandableListView"
+ relatedTo="ListView"
+ fill="width_in_vertical" />
+ <view
+ class="android.widget.GridView"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="android.widget.ScrollView"
+ relatedTo="HorizontalScrollView"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="android.widget.HorizontalScrollView"
+ relatedTo="ScrollView"
+ render="skip" />
+ <view
+ class="android.widget.SearchView"
+ render="skip" />
+ <view
+ class="android.widget.SlidingDrawer" />
+ <view
+ class="android.widget.TabHost"
+ fill="width_in_vertical"
+ render="alone" />
+ <view
+ class="android.widget.TabWidget"
+ render="alone" />
+ <view
+ class="android.webkit.WebView"
+ fill="opposite"
+ render="skip" />
</category>
- <category name="Time &amp; Date">
- <view class="android.widget.TimePicker" render="alone"/>
- <view class="android.widget.DatePicker" render="alone"/>
- <view class="android.widget.CalendarView" />
- <view class="android.widget.Chronometer" />
- <view class="android.widget.AnalogClock" />
- <view class="android.widget.DigitalClock" />
+ <category
+ name="Images &amp; Media">
+ <view
+ class="android.widget.ImageView"
+ relatedTo="ImageButton,VideoView" />
+ <view
+ class="android.widget.ImageButton"
+ relatedTo="Button,ImageView" />
+ <view
+ class="android.widget.Gallery"
+ fill="width_in_vertical"
+ render="skip" />
+ <view
+ class="android.widget.MediaController"
+ render="skip" />
+ <view
+ class="android.widget.VideoView"
+ relatedTo="ImageView"
+ fill="opposite"
+ render="skip" />
</category>
- <category name="Transitions">
- <view class="android.widget.ImageSwitcher" render="skip"/>
- <view class="android.widget.AdapterViewFlipper" fill="opposite" render="skip"/>
- <view class="android.widget.StackView" fill="opposite" render="skip"/>
- <view class="android.widget.TextSwitcher" fill="opposite" render="skip"/>
- <view class="android.widget.ViewAnimator" fill="opposite" render="skip"/>
- <view class="android.widget.ViewFlipper" fill="opposite" render="skip"/>
- <view class="android.widget.ViewSwitcher" fill="opposite" render="skip"/>
+ <category
+ name="Time &amp; Date">
+ <view
+ class="android.widget.TimePicker"
+ relatedTo="DatePicker,CalendarView"
+ render="alone" />
+ <view
+ class="android.widget.DatePicker"
+ relatedTo="TimePicker"
+ render="alone" />
+ <view
+ class="android.widget.CalendarView"
+ fill="both"
+ relatedTo="TimePicker,DatePicker" />
+ <view
+ class="android.widget.Chronometer"
+ render="skip" />
+ <view
+ class="android.widget.AnalogClock"
+ relatedTo="DigitalClock" />
+ <view
+ class="android.widget.DigitalClock"
+ relatedTo="AnalogClock" />
</category>
- <category name="Advanced">
- <view class="android.view.View" render="skip"/>
- <view class="android.view.ViewStub" render="skip"/>
- <view class="android.gesture.GestureOverlayView" render="skip"/>
- <view class="android.view.SurfaceView" render="skip"/>
- <view class="android.widget.NumberPicker" render="alone"/>
- <view class="android.widget.ZoomButton"/>
- <view class="android.widget.ZoomControls"/>
- <view class="include" skip="true" render="skip"/>
- <view class="merge" skip="true" render="skip"/>
- <view class="android.widget.DialerFilter" fill="width_in_vertical" render="skip"/>
- <view class="android.widget.AbsoluteLayout" fill="opposite" render="skip"/>
+ <category
+ name="Transitions">
+ <view
+ class="android.widget.ImageSwitcher"
+ relatedTo="ViewFlipper,ViewSwitcher,TextSwitcher"
+ render="skip" />
+ <view
+ class="android.widget.AdapterViewFlipper"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="android.widget.StackView"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="android.widget.TextSwitcher"
+ relatedTo="ViewFlipper,ImageSwitcher,ViewSwitcher"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="android.widget.ViewAnimator"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="android.widget.ViewFlipper"
+ relatedTo="ViewSwitcher,ImageSwitcher,TextSwitcher"
+ fill="opposite"
+ render="skip" />
+ <view
+ class="android.widget.ViewSwitcher"
+ relatedTo="ViewFlipper,ImageSwitcher,TextSwitcher"
+ fill="opposite"
+ render="skip" />
</category>
- <category name="Other">
+ <category
+ name="Advanced">
+ <view
+ class="requestFocus"
+ render="skip" />
+ <view
+ class="android.view.View"
+ render="skip" />
+ <view
+ class="android.view.ViewStub"
+ render="skip" />
+ <view
+ class="android.gesture.GestureOverlayView"
+ render="skip" />
+ <view
+ class="android.view.SurfaceView"
+ render="skip" />
+ <view
+ class="android.widget.NumberPicker"
+ relatedTo="TimePicker,DatePicker"
+ render="alone" />
+ <view
+ class="android.widget.ZoomButton"
+ relatedTo="Button" />
+ <view
+ class="android.widget.ZoomControls" />
+ <view
+ class="merge"
+ skip="true"
+ render="skip" />
+ <view
+ class="android.widget.DialerFilter"
+ fill="width_in_vertical"
+ render="skip" />
+ <view
+ class="android.widget.TwoLineListItem"
+ render="skip" />
+ <view
+ class="android.widget.AbsoluteLayout"
+ fill="opposite"
+ render="skip" />
+ </category>
+ <category
+ name="Other">
<!-- This is the catch-all category which contains unknown views if we encounter any -->
</category>
<!-- TODO: Add-ons? -->
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/rendering-configs.xml b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/rendering-configs.xml
index 9c99644..97cda6e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/rendering-configs.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/rendering-configs.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Default configuration for various views to be rendered
+ Default configuration for various views to be rendered
TODO: Remove views that don't have custom configuration
TODO: Parameterize the custom width (200dip) in the below?
-->
@@ -39,24 +39,148 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content">
</CheckedTextView>
+ <!--
<Chronometer
android:text="Chronometer"
android:id="@+id/android_widget_Chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Chronometer>
+ -->
<DigitalClock
android:text="DigitalClock"
android:id="@+id/android_widget_DigitalClock"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</DigitalClock>
+
<EditText
- android:id="@+id/android_widget_EditText"
- android:layout_height="wrap_content"
- android:text="EditText"
- android:layout_width="200dip">
+ android:id="@+id/PlainText"
+ android:text="abc"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Password"
+ android:inputType="textPassword"
+ android:text="••••••••"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <!-- android:inputType="numberPassword" not used here to allow digits in preview only -->
+ <EditText
+ android:id="@+id/PasswordNumeric"
+ android:text="1•••2•••3"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/PersonName"
+ android:inputType="textPersonName"
+ android:text="Firstname Lastname"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Phone"
+ android:inputType="phone"
+ android:text="(555) 0100"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/PostalAddress"
+ android:inputType="textPostalAddress"
+ android:text="Address"
+ android:layout_width="200dip"
+ android:layout_height="100dip">
+ </EditText>
+
+ <EditText
+ android:id="@+id/MultilineText"
+ android:inputType="textMultiLine"
+ android:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"
+ android:layout_width="200dip"
+ android:layout_height="100dip">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Date"
+ android:inputType="date"
+ android:text="1/1/2011"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Time"
+ android:inputType="time"
+ android:text="12:00am"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Email"
+ android:inputType="textEmailAddress"
+ android:text="user@domain"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
</EditText>
+
+ <EditText
+ android:id="@+id/Number"
+ android:inputType="number"
+ android:text="42"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/NumberSigned"
+ android:inputType="numberSigned"
+ android:text="-42"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/NumberDecimal"
+ android:inputType="numberDecimal"
+ android:text="42.0"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <TextView
+ android:text="Large"
+ android:id="@+id/LargeText"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ </TextView>
+
+ <TextView
+ android:text="Medium"
+ android:id="@+id/MediumText"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ </TextView>
+
+ <TextView
+ android:text="Small"
+ android:id="@+id/SmallText"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ </TextView>
+
<ImageButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -76,10 +200,29 @@
android:id="@+id/android_widget_MultiAutoCompleteTextView">
</MultiAutoCompleteTextView>
<ProgressBar
- android:id="@+id/android_widget_ProgressBar"
+ android:id="@+id/android_widget_ProgressBarNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ProgressBar>
+ <ProgressBar
+ android:id="@+id/android_widget_ProgressBarHorizontal"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content"
+ android:progress="30"
+ style="?android:attr/progressBarStyleHorizontal">
+ </ProgressBar>
+ <ProgressBar
+ android:id="@+id/android_widget_ProgressBarLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/progressBarStyleLarge">
+ </ProgressBar>
+ <ProgressBar
+ android:id="@+id/android_widget_ProgressBarSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/progressBarStyleSmall">
+ </ProgressBar>
<QuickContactBadge
android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -104,6 +247,22 @@
android:layout_width="200dip"
android:progress="30">
</SeekBar>
+ <ListView
+ android:id="@+id/android_widget_ListView"
+ android:layout_width="200dip"
+ android:layout_height="60dip"
+ android:divider="#333333"
+ android:dividerHeight="1px"
+ >
+ </ListView>
+ <ExpandableListView
+ android:id="@+id/android_widget_ExpandableListView"
+ android:layout_width="200dip"
+ android:layout_height="60dip"
+ android:divider="#333333"
+ android:dividerHeight="1px"
+ >
+ </ExpandableListView>
<Spinner
android:layout_height="wrap_content"
android:id="@+id/android_widget_Spinner"
@@ -143,6 +302,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</DatePicker>
+ <CalendarView
+ android:id="@+id/android_widget_CalendarView"
+ android:layout_width="200dip"
+ android:layout_height="200dip">
+ </CalendarView>
<RadioGroup
android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -216,4 +380,4 @@
</FrameLayout>
</LinearLayout>
</TabHost>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutAction.java
new file mode 100644
index 0000000..7029be4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutAction.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+
+/**
+ * Action executed when the "Convert Layout" menu item is invoked.
+ */
+public class ChangeLayoutAction extends VisualRefactoringAction {
+ @Override
+ public void run(IAction action) {
+ if ((mTextSelection != null || mTreeSelection != null) && mFile != null) {
+ ChangeLayoutRefactoring ref = new ChangeLayoutRefactoring(mFile, mEditor,
+ mTextSelection, mTreeSelection);
+ RefactoringWizard wizard = new ChangeLayoutWizard(ref, mEditor);
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ op.run(mWindow.getShell(), wizard.getDefaultPageTitle());
+ } catch (InterruptedException e) {
+ // Interrupted. Pass.
+ }
+ }
+ }
+
+ public static IAction create(LayoutEditor editor) {
+ return create("Change Layout...", editor, ChangeLayoutAction.class);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutContribution.java
new file mode 100644
index 0000000..c508b7e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutContribution.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+
+import java.util.Map;
+
+public class ChangeLayoutContribution extends RefactoringContribution {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public RefactoringDescriptor createDescriptor(String id, String project, String description,
+ String comment, Map arguments, int flags) throws IllegalArgumentException {
+ return new ChangeLayoutRefactoring.Descriptor(project, description, comment, arguments);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map retrieveArgumentMap(RefactoringDescriptor descriptor) {
+ if (descriptor instanceof ChangeLayoutRefactoring.Descriptor) {
+ return ((ChangeLayoutRefactoring.Descriptor) descriptor).getArguments();
+ }
+ return super.retrieveArgumentMap(descriptor);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoring.java
new file mode 100644
index 0000000..fc3b21f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoring.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_BASELINE_ALIGNED;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_BASELINE;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_BELOW;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_TO_RIGHT_OF;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_GESTURE_OVERLAY_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_LINEAR_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_RELATIVE_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_TABLE_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.GESTURE_OVERLAY_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.LINEAR_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.TABLE_ROW;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_FALSE;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ViewHierarchy;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Converts the selected layout into a layout of a different type.
+ */
+@SuppressWarnings("restriction") // XML model
+public class ChangeLayoutRefactoring extends VisualRefactoring {
+ private static final String KEY_TYPE = "type"; //$NON-NLS-1$
+ private static final String KEY_FLATTEN = "flatten"; //$NON-NLS-1$
+
+ private String mTypeFqcn;
+ private boolean mFlatten;
+
+ /**
+ * This constructor is solely used by {@link Descriptor},
+ * to replay a previous refactoring.
+ * @param arguments argument map created by #createArgumentMap.
+ */
+ ChangeLayoutRefactoring(Map<String, String> arguments) {
+ super(arguments);
+ mTypeFqcn = arguments.get(KEY_TYPE);
+ mFlatten = Boolean.parseBoolean(arguments.get(KEY_FLATTEN));
+ }
+
+ @VisibleForTesting
+ ChangeLayoutRefactoring(List<Element> selectedElements, LayoutEditor editor) {
+ super(selectedElements, editor);
+ }
+
+ public ChangeLayoutRefactoring(IFile file, LayoutEditor editor, ITextSelection selection,
+ ITreeSelection treeSelection) {
+ super(file, editor, selection, treeSelection);
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = new RefactoringStatus();
+
+ try {
+ pm.beginTask("Checking preconditions...", 2);
+
+ if (mSelectionStart == -1 || mSelectionEnd == -1) {
+ status.addFatalError("No selection to convert");
+ return status;
+ }
+
+ if (mElements.size() != 1) {
+ status.addFatalError("Select precisely one layout to convert");
+ return status;
+ }
+
+ pm.worked(1);
+ return status;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ @Override
+ protected VisualRefactoringDescriptor createDescriptor() {
+ String comment = getName();
+ return new Descriptor(
+ mProject.getName(), //project
+ comment, //description
+ comment, //comment
+ createArgumentMap());
+ }
+
+ @Override
+ protected Map<String, String> createArgumentMap() {
+ Map<String, String> args = super.createArgumentMap();
+ args.put(KEY_TYPE, mTypeFqcn);
+ args.put(KEY_FLATTEN, Boolean.toString(mFlatten));
+
+ return args;
+ }
+
+ @Override
+ public String getName() {
+ return "Change Layout";
+ }
+
+ void setType(String typeFqcn) {
+ mTypeFqcn = typeFqcn;
+ }
+
+ void setFlatten(boolean flatten) {
+ mFlatten = flatten;
+ }
+
+ @Override
+ protected List<Element> initElements() {
+ List<Element> elements = super.initElements();
+
+ // Don't convert a root GestureOverlayView; convert its child. This looks for
+ // gesture overlays, and if found, it generates a new child list where the gesture
+ // overlay children are replaced by their first element children
+ for (Element element : elements) {
+ String tagName = element.getTagName();
+ if (tagName.equals(GESTURE_OVERLAY_VIEW)
+ || tagName.equals(FQCN_GESTURE_OVERLAY_VIEW)) {
+ List<Element> replacement = new ArrayList<Element>(elements.size());
+ for (Element e : elements) {
+ tagName = e.getTagName();
+ if (tagName.equals(GESTURE_OVERLAY_VIEW)
+ || tagName.equals(FQCN_GESTURE_OVERLAY_VIEW)) {
+ NodeList children = e.getChildNodes();
+ Element first = null;
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ first = (Element) node;
+ break;
+ }
+ }
+ if (first != null) {
+ e = first;
+ }
+ }
+ replacement.add(e);
+ }
+ return replacement;
+ }
+ }
+
+ return elements;
+ }
+
+ @Override
+ protected List<Change> computeChanges(IProgressMonitor monitor) {
+ String name = getViewClass(mTypeFqcn);
+
+ IFile file = mEditor.getInputFile();
+ List<Change> changes = new ArrayList<Change>();
+ TextFileChange change = new TextFileChange(file.getName(), file);
+ MultiTextEdit rootEdit = new MultiTextEdit();
+ change.setEdit(rootEdit);
+ change.setTextType(EXT_XML);
+ changes.add(change);
+
+ String text = getText(mSelectionStart, mSelectionEnd);
+ Element layout = getPrimaryElement();
+ String oldName = layout.getNodeName();
+ int open = text.indexOf(oldName);
+ int close = text.lastIndexOf(oldName);
+
+ if (open != -1 && close != -1) {
+ int oldLength = oldName.length();
+ rootEdit.addChild(new ReplaceEdit(mSelectionStart + open, oldLength, name));
+ if (close != open) { // Gracefully handle <FooLayout/>
+ rootEdit.addChild(new ReplaceEdit(mSelectionStart + close, oldLength, name));
+ }
+ }
+
+ String oldId = getId(layout);
+ String newId = ensureIdMatchesType(layout, mTypeFqcn, rootEdit);
+ // Update any layout references to the old id with the new id
+ if (oldId != null && newId != null) {
+ IStructuredModel model = mEditor.getModelForRead();
+ try {
+ IStructuredDocument doc = model.getStructuredDocument();
+ if (doc != null) {
+ List<TextEdit> replaceIds = replaceIds(getAndroidNamespacePrefix(), doc,
+ mSelectionStart,
+ mSelectionEnd, oldId, newId);
+ for (TextEdit edit : replaceIds) {
+ rootEdit.addChild(edit);
+ }
+ }
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+
+ String oldType = getOldType();
+ String newType = mTypeFqcn;
+ if (newType.equals(FQCN_RELATIVE_LAYOUT)) {
+ if (oldType.equals(FQCN_LINEAR_LAYOUT) && !mFlatten) {
+ // Hand-coded conversion specifically tailored for linear to relative, provided
+ // there is no hierarchy flattening
+ // TODO: use the RelativeLayoutConversionHelper for this; it does a better job
+ // analyzing gravities etc.
+ convertLinearToRelative(rootEdit);
+ removeUndefinedLayoutAttrs(rootEdit, layout);
+ } else {
+ // Generic conversion to relative - can also flatten the hierarchy
+ convertAnyToRelative(rootEdit);
+ // This already handles removing undefined layout attributes -- right?
+ //removeUndefinedLayoutAttrs(rootEdit, layout);
+ }
+ } else if (oldType.equals(FQCN_RELATIVE_LAYOUT) && newType.equals(FQCN_LINEAR_LAYOUT)) {
+ convertRelativeToLinear(rootEdit);
+ removeUndefinedLayoutAttrs(rootEdit, layout);
+ } else if (oldType.equals(FQCN_LINEAR_LAYOUT) && newType.equals(FQCN_TABLE_LAYOUT)) {
+ convertLinearToTable(rootEdit);
+ removeUndefinedLayoutAttrs(rootEdit, layout);
+ } else {
+ convertGeneric(rootEdit, oldType, newType);
+ removeUndefinedLayoutAttrs(rootEdit, layout);
+ }
+
+ return changes;
+ }
+
+ /** Hand coded conversion from a LinearLayout to a TableLayout */
+ private void convertLinearToTable(MultiTextEdit rootEdit) {
+ // This is pretty easy; just switch the root tag (already done by the initial generic
+ // conversion) and then convert all the children into <TableRow> elements.
+ // Finally, get rid of the orientation attribute, if any.
+ Element layout = getPrimaryElement();
+ removeOrientationAttribute(rootEdit, layout);
+
+ NodeList children = layout.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element child = (Element) node;
+ if (node instanceof IndexedRegion) {
+ IndexedRegion region = (IndexedRegion) node;
+ int start = region.getStartOffset();
+ int end = region.getEndOffset();
+ String text = getText(start, end);
+ String oldName = child.getNodeName();
+ if (oldName.equals(LINEAR_LAYOUT)) {
+ removeOrientationAttribute(rootEdit, child);
+ int open = text.indexOf(oldName);
+ int close = text.lastIndexOf(oldName);
+
+ if (open != -1 && close != -1) {
+ int oldLength = oldName.length();
+ rootEdit.addChild(new ReplaceEdit(mSelectionStart + open, oldLength,
+ TABLE_ROW));
+ if (close != open) { // Gracefully handle <FooLayout/>
+ rootEdit.addChild(new ReplaceEdit(mSelectionStart + close,
+ oldLength, TABLE_ROW));
+ }
+ }
+ } // else: WRAP in TableLayout!
+ }
+ }
+ }
+ }
+
+ /** Hand coded conversion from a LinearLayout to a RelativeLayout */
+ private void convertLinearToRelative(MultiTextEdit rootEdit) {
+ // This can be done accurately.
+ Element layout = getPrimaryElement();
+ // Horizontal is the default, so if no value is specified it is horizontal.
+ boolean isVertical = VALUE_VERTICAL.equals(layout.getAttributeNS(ANDROID_URI,
+ ATTR_ORIENTATION));
+
+ removeOrientationAttribute(rootEdit, layout);
+
+ String attributePrefix = getAndroidNamespacePrefix();
+
+ // TODO: Consider gravity of each element
+ // TODO: Consider weight of each element
+ // Right now it simply makes a single attachment to keep the order.
+
+ if (isVertical) {
+ // Align each child to the bottom and left of its parent
+ NodeList children = layout.getChildNodes();
+ String prevId = null;
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element child = (Element) node;
+ String id = ensureHasId(rootEdit, child, null);
+ if (prevId != null) {
+ setAttribute(rootEdit, child, ANDROID_URI, attributePrefix,
+ ATTR_LAYOUT_BELOW, prevId);
+ }
+ prevId = id;
+ }
+ }
+ } else {
+ // Align each child to the left
+ NodeList children = layout.getChildNodes();
+ boolean isBaselineAligned =
+ !VALUE_FALSE.equals(layout.getAttributeNS(ANDROID_URI, ATTR_BASELINE_ALIGNED));
+
+ String prevId = null;
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element child = (Element) node;
+ String id = ensureHasId(rootEdit, child, null);
+ if (prevId != null) {
+ setAttribute(rootEdit, child, ANDROID_URI, attributePrefix,
+ ATTR_LAYOUT_TO_RIGHT_OF, prevId);
+ if (isBaselineAligned) {
+ setAttribute(rootEdit, child, ANDROID_URI, attributePrefix,
+ ATTR_LAYOUT_ALIGN_BASELINE, prevId);
+ }
+ }
+ prevId = id;
+ }
+ }
+ }
+ }
+
+ /** Strips out the android:orientation attribute from the given linear layout element */
+ private void removeOrientationAttribute(MultiTextEdit rootEdit, Element layout) {
+ assert layout.getTagName().equals(LINEAR_LAYOUT);
+ removeAttribute(rootEdit, layout, ANDROID_URI, ATTR_ORIENTATION);
+ }
+
+ /**
+ * Hand coded conversion from a RelativeLayout to a LinearLayout
+ *
+ * @param rootEdit the root multi text edit to add edits to
+ */
+ private void convertRelativeToLinear(MultiTextEdit rootEdit) {
+ // This is going to be lossy...
+ // TODO: Attempt to "order" the items based on their visual positions
+ // and insert them in that order in the LinearLayout.
+ // TODO: Possibly use nesting if necessary, by spatial subdivision,
+ // to accomplish roughly the same layout as the relative layout specifies.
+ }
+
+ /**
+ * Hand coded -generic- conversion from one layout to another. This is not going to be
+ * an accurate layout transformation; instead it simply migrates the layout attributes
+ * that are supported, and adds defaults for any new required layout attributes. In
+ * addition, it attempts to order the children visually based on where they fit in a
+ * rendering. (Unsupported layout attributes will be removed by the caller at the
+ * end.)
+ * <ul>
+ * <li>Try to handle nesting. Converting a *hierarchy* of layouts into a flatter
+ * layout for powerful layouts that support it, like RelativeLayout.
+ * <li>Try to do automatic "inference" about the layout. I can render it and look at
+ * the ViewInfo positions and sizes. I can render it multiple times, at different
+ * sizes, to infer "stretchiness" and "weight" properties of the children.
+ * <li>Try to do indirect transformations. E.g. if I can go from A to B, and B to C,
+ * then an attempt to go from A to C should perform conversions A to B and then B to
+ * C.
+ * </ul>
+ *
+ * @param rootEdit the root multi text edit to add edits to
+ * @param oldType the fully qualified class name of the layout type we are converting
+ * from
+ * @param newType the fully qualified class name of the layout type we are converting
+ * to
+ */
+ private void convertGeneric(MultiTextEdit rootEdit, String oldType, String newType) {
+ // TODO: Add hooks for 3rd party conversions getting registered through the
+ // IViewRule interface.
+
+ // For now we simply go with the default behavior, which is to just strip the
+ // layout attributes that aren't supported.
+ }
+
+ /** Removes all the unused attributes after a conversion */
+ private void removeUndefinedLayoutAttrs(MultiTextEdit rootEdit, Element layout) {
+ ViewElementDescriptor descriptor = getElementDescriptor(mTypeFqcn);
+ if (descriptor == null) {
+ return;
+ }
+
+ Set<String> defined = new HashSet<String>();
+ AttributeDescriptor[] layoutAttributes = descriptor.getLayoutAttributes();
+ for (AttributeDescriptor attribute : layoutAttributes) {
+ defined.add(attribute.getXmlLocalName());
+ }
+
+ NodeList children = layout.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element child = (Element) node;
+
+ List<Attr> attributes = findLayoutAttributes(child);
+ for (Attr attribute : attributes) {
+ String name = attribute.getLocalName();
+ if (!defined.contains(name)) {
+ // Remove it
+ removeAttribute(rootEdit, child, attribute.getNamespaceURI(), name);
+ }
+ }
+ }
+ }
+ }
+
+ /** Hand coded conversion from any layout to a RelativeLayout */
+ private void convertAnyToRelative(MultiTextEdit rootEdit) {
+ // To perform a conversion from any other layout type, including nested conversion,
+ Element layout = getPrimaryElement();
+ CanvasViewInfo rootView = mRootView;
+ if (rootView == null) {
+ LayoutCanvas canvas = mEditor.getGraphicalEditor().getCanvasControl();
+ ViewHierarchy viewHierarchy = canvas.getViewHierarchy();
+ rootView = viewHierarchy.getRoot();
+ }
+
+ RelativeLayoutConversionHelper helper =
+ new RelativeLayoutConversionHelper(this, layout, mFlatten, rootEdit, rootView);
+ helper.convertToRelative();
+ }
+
+ public static class Descriptor extends VisualRefactoringDescriptor {
+ public Descriptor(String project, String description, String comment,
+ Map<String, String> arguments) {
+ super("com.android.ide.eclipse.adt.refactoring.convert", //$NON-NLS-1$
+ project, description, comment, arguments);
+ }
+
+ @Override
+ protected Refactoring createRefactoring(Map<String, String> args) {
+ return new ChangeLayoutRefactoring(args);
+ }
+ }
+
+ String getOldType() {
+ Element primary = getPrimaryElement();
+ if (primary != null) {
+ String oldType = primary.getTagName();
+ if (oldType.indexOf('.') == -1) {
+ oldType = ANDROID_WIDGET_PREFIX + oldType;
+ }
+ return oldType;
+ }
+
+ return null;
+ }
+
+ @VisibleForTesting
+ protected CanvasViewInfo mRootView;
+
+ @VisibleForTesting
+ public void setRootView(CanvasViewInfo rootView) {
+ mRootView = rootView;
+ }
+
+ @Override
+ VisualRefactoringWizard createWizard() {
+ return new ChangeLayoutWizard(this, mEditor);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutWizard.java
new file mode 100644
index 0000000..04da01d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutWizard.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.FQCN_RELATIVE_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.RELATIVE_LAYOUT;
+import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_INCLUDE;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class ChangeLayoutWizard extends VisualRefactoringWizard {
+
+ public ChangeLayoutWizard(ChangeLayoutRefactoring ref, LayoutEditor editor) {
+ super(ref, editor);
+ setDefaultPageTitle("Change Layout");
+ }
+
+ @Override
+ protected void addUserInputPages() {
+ ChangeLayoutRefactoring ref = (ChangeLayoutRefactoring) getRefactoring();
+ String oldType = ref.getOldType();
+ addPage(new InputPage(mEditor.getProject(), oldType));
+ }
+
+ /** Wizard page which inputs parameters for the {@link ChangeLayoutRefactoring} operation */
+ private static class InputPage extends VisualRefactoringInputPage {
+ private final IProject mProject;
+ private final String mOldType;
+ private Combo mTypeCombo;
+ private Button mFlatten;
+ private List<Pair<String, ViewElementDescriptor>> mClassNames;
+
+ public InputPage(IProject project, String oldType) {
+ super("ChangeLayoutInputPage"); //$NON-NLS-1$
+ mProject = project;
+ mOldType = oldType;
+ }
+
+ public void createControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(2, false));
+
+ Label fromLabel = new Label(composite, SWT.NONE);
+ fromLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ String oldTypeBase = mOldType.substring(mOldType.lastIndexOf('.') + 1);
+ fromLabel.setText(String.format("Change from %1$s", oldTypeBase));
+
+ Label typeLabel = new Label(composite, SWT.NONE);
+ typeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ typeLabel.setText("New Layout Type:");
+
+ mTypeCombo = new Combo(composite, SWT.READ_ONLY);
+ mTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ SelectionAdapter selectionListener = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ validatePage();
+ // Hierarchy flattening only works for relative layout (and any future
+ // layouts that can also support arbitrary layouts).
+ mFlatten.setVisible(mTypeCombo.getText().equals(FQCN_RELATIVE_LAYOUT));
+ }
+ };
+ mTypeCombo.addSelectionListener(selectionListener);
+ mTypeCombo.addSelectionListener(mSelectionValidateListener);
+
+ mFlatten = new Button(composite, SWT.CHECK);
+ mFlatten.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER,
+ false, false, 2, 1));
+ mFlatten.setText("Flatten hierarchy");
+ mFlatten.addSelectionListener(selectionListener);
+ // Should flattening be selected by default?
+ mFlatten.setSelection(true);
+ mFlatten.addSelectionListener(mSelectionValidateListener);
+
+ // We don't exclude RelativeLayout even if the current layout is RelativeLayout,
+ // in case you are trying to flatten the hierarchy for a hierarchy that has a
+ // RelativeLayout at the root.
+ Set<String> exclude = new HashSet<String>();
+ exclude.add(VIEW_INCLUDE);
+ boolean oldIsRelativeLayout = mOldType.equals(FQCN_RELATIVE_LAYOUT);
+ if (oldIsRelativeLayout) {
+ exclude.add(mOldType);
+ }
+ mClassNames = WrapInWizard.addLayouts(mProject, mOldType, mTypeCombo, exclude, false);
+
+ mTypeCombo.select(0);
+ // The default should be Relative layout, if available (and not the old Type)
+ if (!oldIsRelativeLayout) {
+ for (int i = 0; i < mTypeCombo.getItemCount(); i++) {
+ if (mTypeCombo.getItem(i).equals(RELATIVE_LAYOUT)) {
+ mTypeCombo.select(i);
+ break;
+ }
+ }
+ }
+ mFlatten.setVisible(mTypeCombo.getText().equals(RELATIVE_LAYOUT));
+
+ setControl(composite);
+ validatePage();
+ }
+
+ @Override
+ protected boolean validatePage() {
+ boolean ok = true;
+
+ int selectionIndex = mTypeCombo.getSelectionIndex();
+ String type = selectionIndex != -1 ? mClassNames.get(selectionIndex).getFirst() : null;
+ if (type == null) {
+ setErrorMessage("Select a layout type");
+ ok = false; // The user has chosen a separator
+ } else {
+ setErrorMessage(null);
+
+ // Record state
+ ChangeLayoutRefactoring refactoring =
+ (ChangeLayoutRefactoring) getRefactoring();
+ refactoring.setType(type);
+ refactoring.setFlatten(mFlatten.getSelection());
+ }
+
+ setPageComplete(ok);
+ return ok;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewAction.java
new file mode 100644
index 0000000..1d9ea86
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewAction.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+
+/**
+ * Action executed when the "Change View Type" menu item is invoked.
+ */
+public class ChangeViewAction extends VisualRefactoringAction {
+ @Override
+ public void run(IAction action) {
+ if ((mTextSelection != null || mTreeSelection != null) && mFile != null) {
+ ChangeViewRefactoring ref = new ChangeViewRefactoring(mFile, mEditor,
+ mTextSelection, mTreeSelection);
+ RefactoringWizard wizard = new ChangeViewWizard(ref, mEditor);
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ op.run(mWindow.getShell(), wizard.getDefaultPageTitle());
+ } catch (InterruptedException e) {
+ // Interrupted. Pass.
+ }
+ }
+ }
+
+ public static IAction create(LayoutEditor editor) {
+ return create("Change Widget Type...", editor, ChangeViewAction.class);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewContribution.java
new file mode 100644
index 0000000..7705ed8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewContribution.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+
+import java.util.Map;
+
+public class ChangeViewContribution extends RefactoringContribution {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public RefactoringDescriptor createDescriptor(String id, String project, String description,
+ String comment, Map arguments, int flags) throws IllegalArgumentException {
+ return new ChangeViewRefactoring.Descriptor(project, description, comment, arguments);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map retrieveArgumentMap(RefactoringDescriptor descriptor) {
+ if (descriptor instanceof ChangeViewRefactoring.Descriptor) {
+ return ((ChangeViewRefactoring.Descriptor) descriptor).getArguments();
+ }
+ return super.retrieveArgumentMap(descriptor);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoring.java
new file mode 100644
index 0000000..2e92e48
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoring.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Changes the type of the given widgets to the given target type
+ * and updates the attributes if necessary
+ */
+@SuppressWarnings("restriction") // XML model
+public class ChangeViewRefactoring extends VisualRefactoring {
+ private static final String KEY_TYPE = "type"; //$NON-NLS-1$
+ private String mTypeFqcn;
+
+ /**
+ * This constructor is solely used by {@link Descriptor},
+ * to replay a previous refactoring.
+ * @param arguments argument map created by #createArgumentMap.
+ */
+ ChangeViewRefactoring(Map<String, String> arguments) {
+ super(arguments);
+ mTypeFqcn = arguments.get(KEY_TYPE);
+ }
+
+ public ChangeViewRefactoring(IFile file, LayoutEditor editor, ITextSelection selection,
+ ITreeSelection treeSelection) {
+ super(file, editor, selection, treeSelection);
+ }
+
+ @VisibleForTesting
+ ChangeViewRefactoring(List<Element> selectedElements, LayoutEditor editor) {
+ super(selectedElements, editor);
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = new RefactoringStatus();
+
+ try {
+ pm.beginTask("Checking preconditions...", 6);
+
+ if (mSelectionStart == -1 || mSelectionEnd == -1) {
+ status.addFatalError("No selection to convert");
+ return status;
+ }
+
+ // Make sure the selection is contiguous
+ if (mTreeSelection != null) {
+ List<CanvasViewInfo> infos = getSelectedViewInfos();
+ if (!validateNotEmpty(infos, status)) {
+ return status;
+ }
+ }
+
+ // Ensures that we have a valid DOM model:
+ if (mElements.size() == 0) {
+ status.addFatalError("Nothing to convert");
+ return status;
+ }
+
+ pm.worked(1);
+ return status;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ @Override
+ protected VisualRefactoringDescriptor createDescriptor() {
+ String comment = getName();
+ return new Descriptor(
+ mProject.getName(), //project
+ comment, //description
+ comment, //comment
+ createArgumentMap());
+ }
+
+ @Override
+ protected Map<String, String> createArgumentMap() {
+ Map<String, String> args = super.createArgumentMap();
+ args.put(KEY_TYPE, mTypeFqcn);
+
+ return args;
+ }
+
+ @Override
+ public String getName() {
+ return "Change Widget Type";
+ }
+
+ void setType(String typeFqcn) {
+ mTypeFqcn = typeFqcn;
+ }
+
+ @Override
+ protected List<Change> computeChanges(IProgressMonitor monitor) {
+ String name = getViewClass(mTypeFqcn);
+
+ IFile file = mEditor.getInputFile();
+ List<Change> changes = new ArrayList<Change>();
+ TextFileChange change = new TextFileChange(file.getName(), file);
+ MultiTextEdit rootEdit = new MultiTextEdit();
+ change.setEdit(rootEdit);
+ change.setTextType(EXT_XML);
+ changes.add(change);
+
+ for (Element element : getElements()) {
+ IndexedRegion region = getRegion(element);
+ String text = getText(region.getStartOffset(), region.getEndOffset());
+ String oldName = element.getNodeName();
+ int open = text.indexOf(oldName);
+ int close = text.lastIndexOf(oldName);
+
+ if (open != -1 && close != -1) {
+ int oldLength = oldName.length();
+ rootEdit.addChild(new ReplaceEdit(region.getStartOffset() + open,
+ oldLength, name));
+ if (close != open) { // Gracefully handle <FooLayout/>
+ rootEdit.addChild(new ReplaceEdit(region.getStartOffset() + close, oldLength,
+ name));
+ }
+ }
+
+ // Change tag type
+ String oldId = getId(element);
+ String newId = ensureIdMatchesType(element, mTypeFqcn, rootEdit);
+ // Update any layout references to the old id with the new id
+ if (oldId != null && newId != null) {
+ IStructuredModel model = mEditor.getModelForRead();
+ try {
+ IStructuredDocument doc = model.getStructuredDocument();
+ if (doc != null) {
+ IndexedRegion range = getRegion(element);
+ int skipStart = range.getStartOffset();
+ int skipEnd = range.getEndOffset();
+ List<TextEdit> replaceIds = replaceIds(getAndroidNamespacePrefix(), doc,
+ skipStart, skipEnd,
+ oldId, newId);
+ for (TextEdit edit : replaceIds) {
+ rootEdit.addChild(edit);
+ }
+ }
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+
+ // Strip out attributes that no longer make sense
+ removeUndefinedAttrs(rootEdit, element);
+ }
+
+ return changes;
+ }
+
+ /** Removes all the unused attributes after a conversion */
+ private void removeUndefinedAttrs(MultiTextEdit rootEdit, Element element) {
+ ViewElementDescriptor descriptor = getElementDescriptor(mTypeFqcn);
+ if (descriptor == null) {
+ return;
+ }
+
+ Set<String> defined = new HashSet<String>();
+ AttributeDescriptor[] layoutAttributes = descriptor.getAttributes();
+ for (AttributeDescriptor attribute : layoutAttributes) {
+ defined.add(attribute.getXmlLocalName());
+ }
+
+ List<Attr> attributes = findAttributes(element);
+ for (Attr attribute : attributes) {
+ String name = attribute.getLocalName();
+ if (!defined.contains(name)) {
+ // Remove it
+ removeAttribute(rootEdit, element, attribute.getNamespaceURI(), name);
+ }
+ }
+
+ // Set text attribute if it's defined
+ if (defined.contains(ATTR_TEXT) && !element.hasAttributeNS(ANDROID_URI, ATTR_TEXT)) {
+ setAttribute(rootEdit, element, ANDROID_URI, getAndroidNamespacePrefix(),
+ ATTR_TEXT, descriptor.getUiName());
+ }
+ }
+
+ protected List<Attr> findAttributes(Node root) {
+ List<Attr> result = new ArrayList<Attr>();
+ NamedNodeMap attributes = root.getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Node attributeNode = attributes.item(i);
+
+ String name = attributeNode.getLocalName();
+ if (!name.startsWith(ATTR_LAYOUT_PREFIX)
+ && ANDROID_URI.equals(attributeNode.getNamespaceURI())) {
+ result.add((Attr) attributeNode);
+ }
+ }
+
+ return result;
+ }
+
+ List<String> getOldTypes() {
+ List<String> types = new ArrayList<String>();
+ for (Element primary : getElements()) {
+ String oldType = primary.getTagName();
+ if (oldType.indexOf('.') == -1) {
+ oldType = ANDROID_WIDGET_PREFIX + oldType;
+ }
+ types.add(oldType);
+ }
+
+ return types;
+ }
+
+ @Override
+ VisualRefactoringWizard createWizard() {
+ return new ChangeViewWizard(this, mEditor);
+ }
+
+ public static class Descriptor extends VisualRefactoringDescriptor {
+ public Descriptor(String project, String description, String comment,
+ Map<String, String> arguments) {
+ super("com.android.ide.eclipse.adt.refactoring.changeview", //$NON-NLS-1$
+ project, description, comment, arguments);
+ }
+
+ @Override
+ protected Refactoring createRefactoring(Map<String, String> args) {
+ return new ChangeViewRefactoring(args);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewWizard.java
new file mode 100644
index 0000000..1372006
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewWizard.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CustomViewFinder;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class ChangeViewWizard extends VisualRefactoringWizard {
+ private static final String SEPARATOR_LABEL =
+ "----------------------------------------"; //$NON-NLS-1$
+
+ public ChangeViewWizard(ChangeViewRefactoring ref, LayoutEditor editor) {
+ super(ref, editor);
+ setDefaultPageTitle("Change Widget Type");
+ }
+
+ @Override
+ protected void addUserInputPages() {
+ ChangeViewRefactoring ref = (ChangeViewRefactoring) getRefactoring();
+ List<String> oldTypes = ref.getOldTypes();
+ String oldType = null;
+ for (String type : oldTypes) {
+ if (oldType == null) {
+ oldType = type;
+ } else if (!oldType.equals(type)) {
+ // If the types differ, don't offer related categories
+ oldType = null;
+ break;
+ }
+ }
+ addPage(new InputPage(mEditor.getProject(), oldType));
+ }
+
+ /** Wizard page which inputs parameters for the {@link ChangeViewRefactoring} operation */
+ private static class InputPage extends VisualRefactoringInputPage {
+ private final IProject mProject;
+ private Combo mTypeCombo;
+ private final String mOldType;
+ private List<String> mClassNames;
+
+ public InputPage(IProject project, String oldType) {
+ super("ChangeViewInputPage"); //$NON-NLS-1$
+ mProject = project;
+ mOldType = oldType;
+ }
+
+ public void createControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(2, false));
+
+ Label typeLabel = new Label(composite, SWT.NONE);
+ typeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ typeLabel.setText("New Widget Type:");
+
+ mTypeCombo = new Combo(composite, SWT.READ_ONLY);
+ mTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mTypeCombo.addSelectionListener(mSelectionValidateListener);
+
+ mClassNames = getWidgetTypes(mOldType, mTypeCombo);
+ mTypeCombo.select(0);
+
+ setControl(composite);
+ validatePage();
+
+ mTypeCombo.setFocus();
+ }
+
+ private List<String> getWidgetTypes(String oldType, Combo combo) {
+ List<String> classNames = new ArrayList<String>();
+
+ // Populate type combo
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IAndroidTarget target = currentSdk.getTarget(mProject);
+ if (target != null) {
+ // Try to pick "related" widgets to the one you have selected.
+ // For example, for an AnalogClock, display DigitalClock first.
+ // For a Text, offer EditText, AutoComplete, etc.
+ if (oldType != null) {
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+ List<String> relatedTo = repository.getRelatedTo(oldType);
+ if (relatedTo.size() > 0) {
+ for (String className : relatedTo) {
+ String base = className.substring(className.lastIndexOf('.') + 1);
+ combo.add(base);
+ classNames.add(className);
+ }
+ combo.add(SEPARATOR_LABEL);
+ classNames.add(null);
+ }
+ }
+
+ Pair<List<String>,List<String>> result =
+ CustomViewFinder.findViews(mProject, false);
+ List<String> customViews = result.getFirst();
+ List<String> thirdPartyViews = result.getSecond();
+ if (customViews.size() > 0) {
+ for (String view : customViews) {
+ combo.add(view);
+ classNames.add(view);
+ }
+ combo.add(SEPARATOR_LABEL);
+ classNames.add(null);
+ }
+
+ if (thirdPartyViews.size() > 0) {
+ for (String view : thirdPartyViews) {
+ combo.add(view);
+ classNames.add(view);
+ }
+ combo.add(SEPARATOR_LABEL);
+ classNames.add(null);
+ }
+
+ AndroidTargetData targetData = currentSdk.getTargetData(target);
+ if (targetData != null) {
+ // Now add ALL known layout descriptors in case the user has
+ // a special case
+ List<ViewElementDescriptor> descriptors =
+ targetData.getLayoutDescriptors().getViewDescriptors();
+ for (ViewElementDescriptor d : descriptors) {
+ String className = d.getFullClassName();
+ if (className.equals(LayoutDescriptors.VIEW_INCLUDE)) {
+ continue;
+ }
+ combo.add(d.getUiName());
+ classNames.add(className);
+
+ }
+ }
+ }
+ } else {
+ combo.add("SDK not initialized");
+ classNames.add(null);
+ }
+
+ return classNames;
+ }
+
+ @Override
+ protected boolean validatePage() {
+ boolean ok = true;
+ int selectionIndex = mTypeCombo.getSelectionIndex();
+ String type = selectionIndex != -1 ? mClassNames.get(selectionIndex) : null;
+ if (type == null) {
+ setErrorMessage("Select a widget type to convert to");
+ ok = false; // The user has chosen a separator
+ } else {
+ setErrorMessage(null);
+ }
+
+ // Record state
+ ChangeViewRefactoring refactoring =
+ (ChangeViewRefactoring) getRefactoring();
+ refactoring.setType(type);
+
+ setPageComplete(ok);
+ return ok;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeAction.java
new file mode 100644
index 0000000..09cb044
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeAction.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+
+/**
+ * Action executed when the "Extract as Include" menu item is invoked.
+ */
+public class ExtractIncludeAction extends VisualRefactoringAction {
+ @Override
+ public void run(IAction action) {
+ if ((mTextSelection != null || mTreeSelection != null) && mFile != null) {
+ ExtractIncludeRefactoring ref = new ExtractIncludeRefactoring(mFile, mEditor,
+ mTextSelection, mTreeSelection);
+ RefactoringWizard wizard = new ExtractIncludeWizard(ref, mEditor);
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ op.run(mWindow.getShell(), wizard.getDefaultPageTitle());
+ } catch (InterruptedException e) {
+ // Interrupted. Pass.
+ }
+ }
+ }
+
+ public static IAction create(LayoutEditor editor) {
+ return create("Extract Include...", editor, ExtractIncludeAction.class);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeContribution.java
new file mode 100644
index 0000000..5903812
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeContribution.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+
+import java.util.Map;
+
+public class ExtractIncludeContribution extends RefactoringContribution {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public RefactoringDescriptor createDescriptor(String id, String project, String description,
+ String comment, Map arguments, int flags) throws IllegalArgumentException {
+ return new ExtractIncludeRefactoring.Descriptor(project, description, comment, arguments);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map retrieveArgumentMap(RefactoringDescriptor descriptor) {
+ if (descriptor instanceof ExtractIncludeRefactoring.Descriptor) {
+ return ((ExtractIncludeRefactoring.Descriptor) descriptor).getArguments();
+ }
+ return super.retrieveArgumentMap(descriptor);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java
new file mode 100644
index 0000000..7def205
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.AndroidConstants.FD_RES_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
+import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
+import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS_COLON;
+import static com.android.resources.ResourceType.LAYOUT;
+import static com.android.sdklib.SdkConstants.FD_RES;
+
+import com.android.AndroidConstants;
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
+import com.android.sdklib.SdkConstants;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.NullChange;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Extracts the selection and writes it out as a separate layout file, then adds an
+ * include to that new layout file. Interactively asks the user for a new name for the
+ * layout.
+ */
+@SuppressWarnings("restriction") // XML model
+public class ExtractIncludeRefactoring extends VisualRefactoring {
+ private static final String KEY_NAME = "name"; //$NON-NLS-1$
+ private static final String KEY_OCCURRENCES = "all-occurrences"; //$NON-NLS-1$
+ private String mLayoutName;
+ private boolean mReplaceOccurrences;
+
+ /**
+ * This constructor is solely used by {@link Descriptor},
+ * to replay a previous refactoring.
+ * @param arguments argument map created by #createArgumentMap.
+ */
+ ExtractIncludeRefactoring(Map<String, String> arguments) {
+ super(arguments);
+ mLayoutName = arguments.get(KEY_NAME);
+ mReplaceOccurrences = Boolean.parseBoolean(arguments.get(KEY_OCCURRENCES));
+ }
+
+ public ExtractIncludeRefactoring(IFile file, LayoutEditor editor, ITextSelection selection,
+ ITreeSelection treeSelection) {
+ super(file, editor, selection, treeSelection);
+ }
+
+ @VisibleForTesting
+ ExtractIncludeRefactoring(List<Element> selectedElements, LayoutEditor editor) {
+ super(selectedElements, editor);
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = new RefactoringStatus();
+
+ try {
+ pm.beginTask("Checking preconditions...", 6);
+
+ if (mSelectionStart == -1 || mSelectionEnd == -1) {
+ status.addFatalError("No selection to extract");
+ return status;
+ }
+
+ // Make sure the selection is contiguous
+ if (mTreeSelection != null) {
+ // TODO - don't do this if we based the selection on text. In this case,
+ // make sure we're -balanced-.
+ List<CanvasViewInfo> infos = getSelectedViewInfos();
+ if (!validateNotEmpty(infos, status)) {
+ return status;
+ }
+
+ if (!validateNotRoot(infos, status)) {
+ return status;
+ }
+
+ // Disable if you've selected a single include tag
+ if (infos.size() == 1) {
+ UiViewElementNode uiNode = infos.get(0).getUiViewNode();
+ if (uiNode != null) {
+ Node xmlNode = uiNode.getXmlNode();
+ if (xmlNode.getLocalName().equals(LayoutDescriptors.VIEW_INCLUDE)) {
+ status.addWarning("No point in refactoring a single include tag");
+ }
+ }
+ }
+
+ // Enforce that the selection is -contiguous-
+ if (!validateContiguous(infos, status)) {
+ return status;
+ }
+ }
+
+ // This also ensures that we have a valid DOM model:
+ if (mElements.size() == 0) {
+ status.addFatalError("Nothing to extract");
+ return status;
+ }
+
+ pm.worked(1);
+ return status;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ @Override
+ protected VisualRefactoringDescriptor createDescriptor() {
+ String comment = getName();
+ return new Descriptor(
+ mProject.getName(), //project
+ comment, //description
+ comment, //comment
+ createArgumentMap());
+ }
+
+ @Override
+ protected Map<String, String> createArgumentMap() {
+ Map<String, String> args = super.createArgumentMap();
+ args.put(KEY_NAME, mLayoutName);
+ args.put(KEY_OCCURRENCES, Boolean.toString(mReplaceOccurrences));
+
+ return args;
+ }
+
+ @Override
+ public String getName() {
+ return "Extract as Include";
+ }
+
+ void setLayoutName(String layoutName) {
+ mLayoutName = layoutName;
+ }
+
+ void setReplaceOccurrences(boolean selection) {
+ mReplaceOccurrences = selection;
+ }
+
+ // ---- Actual implementation of Extract as Include modification computation ----
+
+ @Override
+ protected List<Change> computeChanges(IProgressMonitor monitor) {
+ String extractedText = getExtractedText();
+
+ String namespaceDeclarations = computeNamespaceDeclarations();
+
+ // Insert namespace:
+ extractedText = insertNamespace(extractedText, namespaceDeclarations);
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); //$NON-NLS-1$
+ sb.append(extractedText);
+ sb.append('\n');
+
+ List<Change> changes = new ArrayList<Change>();
+
+ String newFileName = mLayoutName + DOT_XML;
+ IProject project = mEditor.getProject();
+ IFile sourceFile = mEditor.getInputFile();
+
+ // Replace extracted elements by <include> tag
+ handleIncludingFile(changes, sourceFile, mSelectionStart, mSelectionEnd,
+ getDomDocument(), getPrimaryElement());
+
+ // Also extract in other variations of the same file (landscape/portrait, etc)
+ boolean haveVariations = false;
+ if (mReplaceOccurrences) {
+ List<IFile> layouts = getOtherLayouts(sourceFile);
+ for (IFile file : layouts) {
+ IModelManager modelManager = StructuredModelManager.getModelManager();
+ IStructuredModel model = null;
+ // We could enhance this with a SubMonitor to make the progress bar move as
+ // well.
+ monitor.subTask(String.format("Looking for duplicates in %1$s",
+ file.getProjectRelativePath()));
+ if (monitor.isCanceled()) {
+ throw new OperationCanceledException();
+ }
+
+ try {
+ model = modelManager.getModelForRead(file);
+ if (model instanceof IDOMModel) {
+ IDOMModel domModel = (IDOMModel) model;
+ IDOMDocument otherDocument = domModel.getDocument();
+ List<Element> otherElements = new ArrayList<Element>();
+ Element otherPrimary = null;
+
+ for (Element element : getElements()) {
+ Element other = DomUtilities.findCorresponding(element,
+ otherDocument);
+ if (other != null) {
+ // See if the structure is similar to what we have in this
+ // document
+ if (DomUtilities.isEquivalent(element, other)) {
+ otherElements.add(other);
+ if (element == getPrimaryElement()) {
+ otherPrimary = other;
+ }
+ }
+ }
+ }
+
+ // Only perform extract in the other file if we find a match for
+ // ALL of elements being extracted, and if they too are contiguous
+ if (otherElements.size() == getElements().size() &&
+ DomUtilities.isContiguous(otherElements)) {
+ // Find the range
+ int begin = Integer.MAX_VALUE;
+ int end = Integer.MIN_VALUE;
+ for (Element element : otherElements) {
+ // Yes!! Extract this one as well!
+ IndexedRegion region = getRegion(element);
+ end = Math.max(end, region.getEndOffset());
+ begin = Math.min(begin, region.getStartOffset());
+ }
+ handleIncludingFile(changes, file, begin,
+ end, otherDocument, otherPrimary);
+ haveVariations = true;
+ }
+ }
+ } catch (IOException e) {
+ AdtPlugin.log(e, null);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+ }
+ }
+
+ // Add change to create the new file
+ IContainer parent = sourceFile.getParent();
+ if (haveVariations) {
+ // If we're extracting from multiple configuration folders, then we need to
+ // place the extracted include in the base layout folder (if not it goes next to
+ // the including file)
+ parent = mProject.getFolder(FD_RES).getFolder(FD_RES_LAYOUT);
+ }
+ IPath parentPath = parent.getProjectRelativePath();
+ final IFile file = project.getFile(new Path(parentPath + WS_SEP + newFileName));
+ TextFileChange addFile = new TextFileChange("Create new separate layout", file);
+ addFile.setTextType(AdtConstants.EXT_XML);
+ changes.add(addFile);
+ addFile.setEdit(new InsertEdit(0, sb.toString()));
+
+ Change finishHook = createFinishHook(file);
+ changes.add(finishHook);
+
+ return changes;
+ }
+
+ private void handleIncludingFile(List<Change> changes,
+ IFile sourceFile, int begin, int end, Document document, Element primary) {
+ TextFileChange change = new TextFileChange(sourceFile.getName(), sourceFile);
+ MultiTextEdit rootEdit = new MultiTextEdit();
+ change.setEdit(rootEdit);
+ change.setTextType(EXT_XML);
+ changes.add(change);
+
+ String referenceId = getReferenceId();
+ // Replace existing elements in the source file and insert <include>
+ String androidNsPrefix = getAndroidNamespacePrefix(document);
+ String include = computeIncludeString(primary, mLayoutName, androidNsPrefix, referenceId);
+ int length = end - begin;
+ ReplaceEdit replace = new ReplaceEdit(begin, length, include);
+ rootEdit.addChild(replace);
+
+ // Update any layout references to the old id with the new id
+ if (referenceId != null && primary != null) {
+ String rootId = getId(primary);
+ IStructuredModel model = null;
+ try {
+ model = StructuredModelManager.getModelManager().getModelForRead(sourceFile);
+ IStructuredDocument doc = model.getStructuredDocument();
+ if (doc != null && rootId != null) {
+ List<TextEdit> replaceIds = replaceIds(androidNsPrefix, doc, begin,
+ end, rootId, referenceId);
+ for (TextEdit edit : replaceIds) {
+ rootEdit.addChild(edit);
+ }
+ }
+ } catch (IOException e) {
+ AdtPlugin.log(e, null);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a list of all the other layouts (in all configurations) in the project other
+ * than the given source layout where the refactoring was initiated. Never null.
+ */
+ private List<IFile> getOtherLayouts(IFile sourceFile) {
+ List<IFile> layouts = new ArrayList<IFile>(100);
+ IPath sourcePath = sourceFile.getProjectRelativePath();
+ IFolder resources = mProject.getFolder(SdkConstants.FD_RESOURCES);
+ try {
+ for (IResource folder : resources.members()) {
+ assert resources != null;
+ if (folder.getName().startsWith(AndroidConstants.FD_RES_LAYOUT) &&
+ folder instanceof IFolder) {
+ IFolder layoutFolder = (IFolder) folder;
+ for (IResource file : layoutFolder.members()) {
+ if (file.getName().endsWith(EXT_XML)
+ && file instanceof IFile) {
+ if (!file.getProjectRelativePath().equals(sourcePath)) {
+ layouts.add((IFile) file);
+ }
+ }
+ }
+ }
+ }
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+
+ return layouts;
+ }
+
+ String getInitialName() {
+ String defaultName = ""; //$NON-NLS-1$
+ Element primary = getPrimaryElement();
+ if (primary != null) {
+ String id = primary.getAttributeNS(ANDROID_URI, ATTR_ID);
+ // id null check for https://bugs.eclipse.org/bugs/show_bug.cgi?id=272378
+ if (id != null && (id.startsWith(ID_PREFIX) || id.startsWith(NEW_ID_PREFIX))) {
+ // Use everything following the id/, and make it lowercase since that is
+ // the convention for layouts
+ defaultName = id.substring(id.indexOf('/') + 1).toLowerCase();
+
+ IInputValidator validator = ResourceNameValidator.create(true, mProject, LAYOUT);
+
+ if (validator.isValid(defaultName) != null) { // Already exists?
+ defaultName = ""; //$NON-NLS-1$
+ }
+ }
+ }
+
+ return defaultName;
+ }
+
+ IFile getSourceFile() {
+ return mFile;
+ }
+
+ private Change createFinishHook(final IFile file) {
+ return new NullChange("Open extracted layout and refresh resources") {
+ @Override
+ public Change perform(IProgressMonitor pm) throws CoreException {
+ Display display = AdtPlugin.getDisplay();
+ display.asyncExec(new Runnable() {
+ public void run() {
+ openFile(file);
+ mEditor.getGraphicalEditor().refreshProjectResources();
+ // Save file to trigger include finder scanning (as well as making
+ // the
+ // actual show-include feature work since it relies on reading
+ // files from
+ // disk, not a live buffer)
+ IWorkbenchPage page = mEditor.getEditorSite().getPage();
+ page.saveEditor(mEditor, false);
+ }
+ });
+
+ // Not undoable: just return null instead of an undo-change.
+ return null;
+ }
+ };
+ }
+
+ private String computeNamespaceDeclarations() {
+ String androidNsPrefix = null;
+ String namespaceDeclarations = null;
+
+ StringBuilder sb = new StringBuilder();
+ List<Attr> attributeNodes = findNamespaceAttributes();
+ for (Node attributeNode : attributeNodes) {
+ String prefix = attributeNode.getPrefix();
+ if (XMLNS.equals(prefix)) {
+ sb.append(' ');
+ String name = attributeNode.getNodeName();
+ sb.append(name);
+ sb.append('=').append('"');
+
+ String value = attributeNode.getNodeValue();
+ if (value.equals(ANDROID_URI)) {
+ androidNsPrefix = name;
+ if (androidNsPrefix.startsWith(XMLNS_COLON)) {
+ androidNsPrefix = androidNsPrefix.substring(XMLNS_COLON.length());
+ }
+ }
+ sb.append(DomUtilities.toXmlAttributeValue(value));
+ sb.append('"');
+ }
+ }
+ namespaceDeclarations = sb.toString();
+
+ if (androidNsPrefix == null) {
+ androidNsPrefix = ANDROID_NS_NAME;
+ }
+
+ if (namespaceDeclarations.length() == 0) {
+ sb.setLength(0);
+ sb.append(' ');
+ sb.append(XMLNS_COLON);
+ sb.append(androidNsPrefix);
+ sb.append('=').append('"');
+ sb.append(ANDROID_URI);
+ sb.append('"');
+ namespaceDeclarations = sb.toString();
+ }
+
+ return namespaceDeclarations;
+ }
+
+ /** Returns the id to be used for the include tag itself (may be null) */
+ private String getReferenceId() {
+ String rootId = getRootId();
+ if (rootId != null) {
+ return rootId + "_ref";
+ }
+
+ return null;
+ }
+
+ /**
+ * Compute the actual {@code <include>} string to be inserted in place of the old
+ * selection
+ */
+ private static String computeIncludeString(Element primaryNode, String newName,
+ String androidNsPrefix, String referenceId) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<include layout=\"@layout/"); //$NON-NLS-1$
+ sb.append(newName);
+ sb.append('"');
+ sb.append(' ');
+
+ // Create new id for the include itself
+ if (referenceId != null) {
+ sb.append(androidNsPrefix);
+ sb.append(':');
+ sb.append(ATTR_ID);
+ sb.append('=').append('"');
+ sb.append(referenceId);
+ sb.append('"').append(' ');
+ }
+
+ // Add id string, unless it's a <merge>, since we may need to adjust any layout
+ // references to apply to the <include> tag instead
+
+ // I should move all the layout_ attributes as well
+ // I also need to duplicate and modify the id and then replace
+ // everything else in the file with this new id...
+
+ // HACK: see issue 13494: We must duplicate the width/height attributes on the
+ // <include> statement for designtime rendering only
+ String width = null;
+ String height = null;
+ if (primaryNode == null) {
+ // Multiple selection - in that case we will be creating an outer <merge>
+ // so we need to set our own width/height on it
+ width = height = VALUE_WRAP_CONTENT;
+ } else {
+ if (!primaryNode.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH)) {
+ width = VALUE_WRAP_CONTENT;
+ } else {
+ width = primaryNode.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH);
+ }
+ if (!primaryNode.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT)) {
+ height = VALUE_WRAP_CONTENT;
+ } else {
+ height = primaryNode.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT);
+ }
+ }
+ if (width != null) {
+ sb.append(' ');
+ sb.append(androidNsPrefix);
+ sb.append(':');
+ sb.append(ATTR_LAYOUT_WIDTH);
+ sb.append('=').append('"');
+ sb.append(DomUtilities.toXmlAttributeValue(width));
+ sb.append('"');
+ }
+ if (height != null) {
+ sb.append(' ');
+ sb.append(androidNsPrefix);
+ sb.append(':');
+ sb.append(ATTR_LAYOUT_HEIGHT);
+ sb.append('=').append('"');
+ sb.append(DomUtilities.toXmlAttributeValue(height));
+ sb.append('"');
+ }
+
+ // Duplicate all the other layout attributes as well
+ if (primaryNode != null) {
+ NamedNodeMap attributes = primaryNode.getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Node attr = attributes.item(i);
+ String name = attr.getLocalName();
+ if (name.startsWith(ATTR_LAYOUT_PREFIX)
+ && ANDROID_URI.equals(attr.getNamespaceURI())) {
+ if (name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT)) {
+ // Already handled
+ continue;
+ }
+
+ sb.append(' ');
+ sb.append(androidNsPrefix);
+ sb.append(':');
+ sb.append(name);
+ sb.append('=').append('"');
+ sb.append(DomUtilities.toXmlAttributeValue(attr.getNodeValue()));
+ sb.append('"');
+ }
+ }
+ }
+
+ sb.append("/>");
+ return sb.toString();
+ }
+
+ /** Return the text in the document in the range start to end */
+ private String getExtractedText() {
+ String xml = getText(mSelectionStart, mSelectionEnd);
+ Element primaryNode = getPrimaryElement();
+ xml = stripTopLayoutAttributes(primaryNode, mSelectionStart, xml);
+ xml = dedent(xml);
+
+ // Wrap siblings in <merge>?
+ if (primaryNode == null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<merge>\n"); //$NON-NLS-1$
+ // indent an extra level
+ for (String line : xml.split("\n")) { //$NON-NLS-1$
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(line).append('\n');
+ }
+ sb.append("</merge>\n"); //$NON-NLS-1$
+ xml = sb.toString();
+ }
+
+ return xml;
+ }
+
+ @Override
+ VisualRefactoringWizard createWizard() {
+ return new ExtractIncludeWizard(this, mEditor);
+ }
+
+ public static class Descriptor extends VisualRefactoringDescriptor {
+ public Descriptor(String project, String description, String comment,
+ Map<String, String> arguments) {
+ super("com.android.ide.eclipse.adt.refactoring.extract.include", //$NON-NLS-1$
+ project, description, comment, arguments);
+ }
+
+ @Override
+ protected Refactoring createRefactoring(Map<String, String> args) {
+ return new ExtractIncludeRefactoring(args);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeWizard.java
new file mode 100644
index 0000000..102390f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeWizard.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
+import com.android.resources.ResourceType;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+class ExtractIncludeWizard extends VisualRefactoringWizard {
+ public ExtractIncludeWizard(ExtractIncludeRefactoring ref, LayoutEditor editor) {
+ super(ref, editor);
+ setDefaultPageTitle(ref.getName());
+ }
+
+ @Override
+ protected void addUserInputPages() {
+ ExtractIncludeRefactoring ref = (ExtractIncludeRefactoring) getRefactoring();
+ String initialName = ref.getInitialName();
+ IFile sourceFile = ref.getSourceFile();
+ addPage(new InputPage(mEditor.getProject(), sourceFile, initialName));
+ }
+
+ /** Wizard page which inputs parameters for the {@link ExtractIncludeRefactoring} operation */
+ private static class InputPage extends VisualRefactoringInputPage {
+ private final IProject mProject;
+ private final IFile mSourceFile;
+ private final String mSuggestedName;
+ private Text mNameText;
+ private Button mReplaceAllOccurrences;
+
+ public InputPage(IProject project, IFile sourceFile, String suggestedName) {
+ super("ExtractIncludeInputPage");
+ mProject = project;
+ mSourceFile = sourceFile;
+ mSuggestedName = suggestedName;
+ }
+
+ public void createControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(2, false));
+
+ Label nameLabel = new Label(composite, SWT.NONE);
+ nameLabel.setText("New Layout Name:");
+ nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+
+ mNameText = new Text(composite, SWT.BORDER);
+ mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mNameText.addModifyListener(mModifyValidateListener);
+
+ mReplaceAllOccurrences = new Button(composite, SWT.CHECK);
+ mReplaceAllOccurrences.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER,
+ false, false, 2, 1));
+ mReplaceAllOccurrences.setText(
+ "Replace occurrences in all layouts with include to new layout");
+ mReplaceAllOccurrences.setEnabled(true);
+ mReplaceAllOccurrences.setSelection(true);
+ mReplaceAllOccurrences.addSelectionListener(mSelectionValidateListener);
+
+ // Initialize UI:
+ if (mSuggestedName != null) {
+ mNameText.setText(mSuggestedName);
+ }
+
+ setControl(composite);
+ validatePage();
+ }
+
+ @Override
+ protected boolean validatePage() {
+ boolean ok = true;
+
+ String text = mNameText.getText().trim();
+
+ if (text.length() == 0) {
+ setErrorMessage("Provide a name for the new layout");
+ ok = false;
+ } else {
+ ResourceNameValidator validator = ResourceNameValidator.create(false, mProject,
+ ResourceType.LAYOUT);
+ String message = validator.isValid(text);
+ if (message != null) {
+ setErrorMessage(message);
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ setErrorMessage(null);
+
+ // Record state
+ ExtractIncludeRefactoring refactoring =
+ (ExtractIncludeRefactoring) getRefactoring();
+ refactoring.setLayoutName(text);
+ refactoring.setReplaceOccurrences(mReplaceAllOccurrences.getSelection());
+ }
+
+ setPageComplete(ok);
+ return ok;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java
new file mode 100644
index 0000000..4eef6b8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+
+/**
+ * Action executed when the "Extract Style" menu item is invoked.
+ */
+public class ExtractStyleAction extends VisualRefactoringAction {
+ @Override
+ public void run(IAction action) {
+ if ((mTextSelection != null || mTreeSelection != null) && mFile != null) {
+ ExtractStyleRefactoring ref = new ExtractStyleRefactoring(mFile, mEditor,
+ mTextSelection, mTreeSelection);
+ RefactoringWizard wizard = new ExtractStyleWizard(ref, mEditor);
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ op.run(mWindow.getShell(), wizard.getDefaultPageTitle());
+ } catch (InterruptedException e) {
+ // Interrupted. Pass.
+ }
+ }
+ }
+
+ public static IAction create(LayoutEditor editor) {
+ return create("Extract Style...", editor, ExtractStyleAction.class);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java
new file mode 100644
index 0000000..95fbdbc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+
+import java.util.Map;
+
+public class ExtractStyleContribution extends RefactoringContribution {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public RefactoringDescriptor createDescriptor(String id, String project, String description,
+ String comment, Map arguments, int flags) throws IllegalArgumentException {
+ return new ExtractStyleRefactoring.Descriptor(project, description, comment, arguments);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map retrieveArgumentMap(RefactoringDescriptor descriptor) {
+ if (descriptor instanceof ExtractStyleRefactoring.Descriptor) {
+ return ((ExtractStyleRefactoring.Descriptor) descriptor).getArguments();
+ }
+ return super.retrieveArgumentMap(descriptor);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java
new file mode 100644
index 0000000..d8dfe86
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.AndroidConstants.FD_RES_VALUES;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_HINT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_MARGIN;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ON_CLICK;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_SRC;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_STYLE;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS_COLON;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.ITEM_TAG;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.NAME_ATTR;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.PARENT_ATTR;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.ROOT_ELEMENT;
+import static com.android.sdklib.SdkConstants.FD_RESOURCES;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * Extracts the selection and writes it out as a separate layout file, then adds an
+ * include to that new layout file. Interactively asks the user for a new name for the
+ * layout.
+ * <p>
+ * Remaining work to do / Possible enhancements:
+ * <ul>
+ * <li>Optionally look in other files in the project and attempt to set style attributes
+ * in other cases where the style attributes match?
+ * <li>If the elements we are extracting from already contain a style attribute, set that
+ * style as the parent style of the current style?
+ * <li>Add a parent-style picker to the wizard (initialized with the above if applicable)
+ * <li>Pick up indentation settings from the XML module
+ * <li>Integrate with themes somehow -- make an option to have the extracted style go into
+ * the theme instead
+ * </ul>
+ */
+@SuppressWarnings("restriction") // XML model
+public class ExtractStyleRefactoring extends VisualRefactoring {
+ private static final String KEY_NAME = "name"; //$NON-NLS-1$
+ private static final String KEY_REMOVE_EXTRACTED = "removeextracted"; //$NON-NLS-1$
+ private static final String KEY_REMOVE_ALL = "removeall"; //$NON-NLS-1$
+ private static final String KEY_APPLY_STYLE = "applystyle"; //$NON-NLS-1$
+ private static final String KEY_PARENT = "parent"; //$NON-NLS-1$
+ private String mStyleName;
+ /** The name of the file in res/values/ that the style will be added to. Normally
+ * res/values/styles.xml - but unit tests pick other names */
+ private String mStyleFileName = "styles.xml";
+ /** Set a style reference on the extracted elements? */
+ private boolean mApplyStyle;
+ /** Remove the attributes that were extracted? */
+ private boolean mRemoveExtracted;
+ /** List of attributes chosen by the user to be extracted */
+ private List<Attr> mChosenAttributes = new ArrayList<Attr>();
+ /** Remove all attributes that match the extracted attributes names, regardless of value */
+ private boolean mRemoveAll;
+ /** The parent style to extend */
+ private String mParent;
+ /** The full list of available attributes in the refactoring */
+ private Map<String, List<Attr>> mAvailableAttributes;
+
+ /**
+ * This constructor is solely used by {@link Descriptor},
+ * to replay a previous refactoring.
+ * @param arguments argument map created by #createArgumentMap.
+ */
+ ExtractStyleRefactoring(Map<String, String> arguments) {
+ super(arguments);
+ mStyleName = arguments.get(KEY_NAME);
+ mRemoveExtracted = Boolean.parseBoolean(arguments.get(KEY_REMOVE_EXTRACTED));
+ mRemoveAll = Boolean.parseBoolean(arguments.get(KEY_REMOVE_ALL));
+ mApplyStyle = Boolean.parseBoolean(arguments.get(KEY_APPLY_STYLE));
+ mParent = arguments.get(KEY_PARENT);
+ }
+
+ public ExtractStyleRefactoring(IFile file, LayoutEditor editor, ITextSelection selection,
+ ITreeSelection treeSelection) {
+ super(file, editor, selection, treeSelection);
+ }
+
+ @VisibleForTesting
+ ExtractStyleRefactoring(List<Element> selectedElements, LayoutEditor editor) {
+ super(selectedElements, editor);
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = new RefactoringStatus();
+
+ try {
+ pm.beginTask("Checking preconditions...", 6);
+
+ if (mSelectionStart == -1 || mSelectionEnd == -1) {
+ status.addFatalError("No selection to extract");
+ return status;
+ }
+
+ // This also ensures that we have a valid DOM model:
+ if (mElements.size() == 0) {
+ status.addFatalError("Nothing to extract");
+ return status;
+ }
+
+ pm.worked(1);
+ return status;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ @Override
+ protected VisualRefactoringDescriptor createDescriptor() {
+ String comment = getName();
+ return new Descriptor(
+ mProject.getName(), //project
+ comment, //description
+ comment, //comment
+ createArgumentMap());
+ }
+
+ @Override
+ protected Map<String, String> createArgumentMap() {
+ Map<String, String> args = super.createArgumentMap();
+ args.put(KEY_NAME, mStyleName);
+ args.put(KEY_REMOVE_EXTRACTED, Boolean.toString(mRemoveExtracted));
+ args.put(KEY_REMOVE_ALL, Boolean.toString(mRemoveAll));
+ args.put(KEY_APPLY_STYLE, Boolean.toString(mApplyStyle));
+ args.put(KEY_PARENT, mParent);
+
+ return args;
+ }
+
+ @Override
+ public String getName() {
+ return "Extract Style";
+ }
+
+ void setStyleName(String styleName) {
+ mStyleName = styleName;
+ }
+
+ void setStyleFileName(String styleFileName) {
+ mStyleFileName = styleFileName;
+ }
+
+ void setChosenAttributes(List<Attr> attributes) {
+ mChosenAttributes = attributes;
+ }
+
+ void setRemoveExtracted(boolean removeExtracted) {
+ mRemoveExtracted = removeExtracted;
+ }
+
+ void setApplyStyle(boolean applyStyle) {
+ mApplyStyle = applyStyle;
+ }
+
+ void setRemoveAll(boolean removeAll) {
+ mRemoveAll = removeAll;
+ }
+
+ void setParent(String parent) {
+ mParent = parent;
+ }
+
+ // ---- Actual implementation of Extract Style modification computation ----
+
+ /**
+ * Returns two items: a map from attribute name to a list of attribute nodes of that
+ * name, and a subset of these attributes that fall within the text selection
+ * (used to drive initial selection in the wizard)
+ */
+ Pair<Map<String, List<Attr>>, Set<Attr>> getAvailableAttributes() {
+ mAvailableAttributes = new TreeMap<String, List<Attr>>();
+ Set<Attr> withinSelection = new HashSet<Attr>();
+ for (Element element : getElements()) {
+ IndexedRegion elementRegion = getRegion(element);
+ boolean allIncluded =
+ (mOriginalSelectionStart <= elementRegion.getStartOffset() &&
+ mOriginalSelectionEnd >= elementRegion.getEndOffset());
+
+ NamedNodeMap attributeMap = element.getAttributes();
+ for (int i = 0, n = attributeMap.getLength(); i < n; i++) {
+ Attr attribute = (Attr) attributeMap.item(i);
+
+ String name = attribute.getLocalName();
+ if (name == null || name.equals(ATTR_ID) || name.startsWith(ATTR_STYLE)
+ || (name.startsWith(ATTR_LAYOUT_PREFIX) &&
+ !name.startsWith(ATTR_LAYOUT_MARGIN))
+ || name.equals(ATTR_TEXT)
+ || name.equals(ATTR_HINT)
+ || name.equals(ATTR_SRC)
+ || name.equals(ATTR_ON_CLICK)) {
+ // Don't offer to extract attributes that don't make sense in
+ // styles (like "id" or "style"), or attributes that the user
+ // probably does not want to define in styles (like layout
+ // attributes such as layout_width, or the label of a button etc).
+ // This makes the options offered listed in the wizard simpler.
+ // In special cases where the user *does* want to set one of these
+ // attributes, they can always do it manually so optimize for
+ // the common case here.
+ continue;
+ }
+
+ // Skip attributes that are in a namespace other than the Android one
+ String namespace = attribute.getNamespaceURI();
+ if (namespace != null && !ANDROID_URI.equals(namespace)) {
+ continue;
+ }
+
+ if (!allIncluded) {
+ IndexedRegion region = getRegion(attribute);
+ boolean attributeIncluded = mOriginalSelectionStart < region.getEndOffset() &&
+ mOriginalSelectionEnd >= region.getStartOffset();
+ if (attributeIncluded) {
+ withinSelection.add(attribute);
+ }
+ } else {
+ withinSelection.add(attribute);
+ }
+
+ List<Attr> list = mAvailableAttributes.get(name);
+ if (list == null) {
+ list = new ArrayList<Attr>();
+ mAvailableAttributes.put(name, list);
+ }
+ list.add(attribute);
+ }
+ }
+
+ return Pair.of(mAvailableAttributes, withinSelection);
+ }
+
+ IFile getStyleFile(IProject project) {
+ return project.getFile(new Path(FD_RESOURCES + WS_SEP + FD_RES_VALUES + WS_SEP
+ + mStyleFileName));
+ }
+
+ @Override
+ protected List<Change> computeChanges(IProgressMonitor monitor) {
+ List<Change> changes = new ArrayList<Change>();
+ if (mChosenAttributes.size() == 0) {
+ return changes;
+ }
+
+ IFile file = getStyleFile(mEditor.getProject());
+ boolean createFile = !file.exists();
+ int insertAtIndex;
+ String initialIndent = null;
+ if (!createFile) {
+ Pair<Integer, String> context = computeInsertContext(file);
+ insertAtIndex = context.getFirst();
+ initialIndent = context.getSecond();
+ } else {
+ insertAtIndex = 0;
+ }
+
+ TextFileChange addFile = new TextFileChange("Create new separate style declaration", file);
+ addFile.setTextType(EXT_XML);
+ changes.add(addFile);
+ String styleString = computeStyleDeclaration(createFile, initialIndent);
+ addFile.setEdit(new InsertEdit(insertAtIndex, styleString));
+
+ // Remove extracted attributes?
+ MultiTextEdit rootEdit = new MultiTextEdit();
+ if (mRemoveExtracted || mRemoveAll) {
+ for (Attr attribute : mChosenAttributes) {
+ List<Attr> list = mAvailableAttributes.get(attribute.getLocalName());
+ for (Attr attr : list) {
+ if (mRemoveAll || attr.getValue().equals(attribute.getValue())) {
+ removeAttribute(rootEdit, attr);
+ }
+ }
+ }
+ }
+
+ // Set the style attribute?
+ if (mApplyStyle) {
+ for (Element element : getElements()) {
+ String value = ResourceResolver.PREFIX_RESOURCE_REF +
+ ResourceResolver.REFERENCE_STYLE + mStyleName;
+ setAttribute(rootEdit, element, null, null, ATTR_STYLE, value);
+ }
+ }
+
+ if (rootEdit.hasChildren()) {
+ IFile sourceFile = mEditor.getInputFile();
+ TextFileChange change = new TextFileChange(sourceFile.getName(), sourceFile);
+ change.setEdit(rootEdit);
+ change.setTextType(EXT_XML);
+ changes.add(change);
+ }
+
+ return changes;
+ }
+
+ private String computeStyleDeclaration(boolean createFile, String initialIndent) {
+ StringBuilder sb = new StringBuilder();
+ if (createFile) {
+ sb.append(NewXmlFileWizard.XML_HEADER_LINE);
+ sb.append('<').append(ROOT_ELEMENT).append(' ');
+ sb.append(XMLNS_COLON).append(ANDROID_NS_NAME).append('=').append('"');
+ sb.append(ANDROID_URI);
+ sb.append('"').append('>').append('\n');
+ }
+
+ // Indent. Use the existing indent found for previous <style> elements in
+ // the resource file - but if that indent was 0 (e.g. <style> elements are
+ // at the left margin) only use it to indent the style elements and use a real
+ // nonzero indent for its children.
+ String indent = " "; //$NON-NLS-1$
+ if (initialIndent == null) {
+ initialIndent = indent;
+ } else if (initialIndent.length() > 0) {
+ indent = initialIndent;
+ }
+ sb.append(initialIndent);
+ String styleTag = "style"; //$NON-NLS-1$ // TODO - use constant in parallel changeset
+ sb.append('<').append(styleTag).append(' ').append(NAME_ATTR).append('=').append('"');
+ sb.append(mStyleName);
+ sb.append('"');
+ if (mParent != null) {
+ sb.append(' ').append(PARENT_ATTR).append('=').append('"');
+ sb.append(mParent);
+ sb.append('"');
+ }
+ sb.append('>').append('\n');
+
+ for (Attr attribute : mChosenAttributes) {
+ sb.append(initialIndent).append(indent);
+ sb.append('<').append(ITEM_TAG).append(' ').append(NAME_ATTR).append('=').append('"');
+ // We've already enforced that regardless of prefix, only attributes with
+ // an Android namespace can be in the set of chosen attributes. Rewrite the
+ // prefix to android here.
+ if (attribute.getPrefix() != null) {
+ sb.append(ANDROID_NS_NAME_PREFIX);
+ }
+ sb.append(attribute.getLocalName());
+ sb.append('"').append('>');
+ sb.append(attribute.getValue());
+ sb.append('<').append('/').append(ITEM_TAG).append('>').append('\n');
+ }
+ sb.append(initialIndent).append('<').append('/').append(styleTag).append('>').append('\n');
+
+ if (createFile) {
+ sb.append('<').append('/').append(ROOT_ELEMENT).append('>').append('\n');
+ }
+ String styleString = sb.toString();
+ return styleString;
+ }
+
+ /** Computes the location in the file to insert the new style element at, as well as
+ * the exact indent string to use to indent the {@code <style>} element.
+ * @param file the styles.xml file to insert into
+ * @return a pair of an insert offset and an indent string
+ */
+ private Pair<Integer, String> computeInsertContext(final IFile file) {
+ int insertAtIndex = -1;
+ // Find the insert of the final </resources> item where we will insert
+ // the new style elements.
+ String indent = null;
+ IModelManager modelManager = StructuredModelManager.getModelManager();
+ IStructuredModel model = null;
+ try {
+ model = modelManager.getModelForRead(file);
+ if (model instanceof IDOMModel) {
+ IDOMModel domModel = (IDOMModel) model;
+ IDOMDocument otherDocument = domModel.getDocument();
+ Element root = otherDocument.getDocumentElement();
+ Node lastChild = root.getLastChild();
+ if (lastChild != null) {
+ if (lastChild instanceof IndexedRegion) {
+ IndexedRegion region = (IndexedRegion) lastChild;
+ insertAtIndex = region.getStartOffset() + region.getLength();
+ }
+
+ // Compute indent
+ while (lastChild != null) {
+ if (lastChild.getNodeType() == Node.ELEMENT_NODE) {
+ IStructuredDocument document = model.getStructuredDocument();
+ indent = AndroidXmlEditor.getIndent(document, lastChild);
+ break;
+ }
+ lastChild = lastChild.getPreviousSibling();
+ }
+ }
+ }
+ } catch (IOException e) {
+ AdtPlugin.log(e, null);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+
+ if (insertAtIndex == -1) {
+ String contents = AdtPlugin.readFile(file);
+ insertAtIndex = contents.indexOf("</" + ROOT_ELEMENT + ">"); //$NON-NLS-1$
+ if (insertAtIndex == -1) {
+ insertAtIndex = contents.length();
+ }
+ }
+
+ return Pair.of(insertAtIndex, indent);
+ }
+
+ @Override
+ VisualRefactoringWizard createWizard() {
+ return new ExtractStyleWizard(this, mEditor);
+ }
+
+ public static class Descriptor extends VisualRefactoringDescriptor {
+ public Descriptor(String project, String description, String comment,
+ Map<String, String> arguments) {
+ super("com.android.ide.eclipse.adt.refactoring.extract.style", //$NON-NLS-1$
+ project, description, comment, arguments);
+ }
+
+ @Override
+ protected Refactoring createRefactoring(Map<String, String> args) {
+ return new ExtractStyleRefactoring(args);
+ }
+ }
+
+ /**
+ * Determines the parent style to be used for this refactoring
+ *
+ * @return the parent style to be used for this refactoring
+ */
+ public String getParentStyle() {
+ Set<String> styles = new HashSet<String>();
+ for (Element element : getElements()) {
+ // Includes "" for elements not setting the style
+ styles.add(element.getAttribute(ATTR_STYLE));
+ }
+
+ if (styles.size() > 1) {
+ // The elements differ in what style attributes they are set to
+ return null;
+ }
+
+ String style = styles.iterator().next();
+ if (style != null && style.length() > 0) {
+ return style;
+ }
+
+ // None of the elements set the style -- see if they have the same widget types
+ // and if so offer to extend the theme style for that widget type
+
+ Set<String> types = new HashSet<String>();
+ for (Element element : getElements()) {
+ types.add(element.getTagName());
+ }
+
+ if (types.size() == 1) {
+ String view = DescriptorsUtils.getBasename(types.iterator().next());
+
+ ResourceResolver resolver = mEditor.getGraphicalEditor().getResourceResolver();
+ // Look up the theme item name, which for a Button would be "buttonStyle", and so on.
+ String n = Character.toLowerCase(view.charAt(0)) + view.substring(1)
+ + "Style"; //$NON-NLS-1$
+ ResourceValue value = resolver.findItemInTheme(n);
+ if (value != null) {
+ ResourceValue resolvedValue = resolver.resolveResValue(value);
+ String name = resolvedValue.getName();
+ if (name != null) {
+ if (resolvedValue.isFramework()) {
+ return ResourceResolver.PREFIX_ANDROID + name;
+ } else {
+ return name;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java
new file mode 100644
index 0000000..7acfe98
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static org.eclipse.jface.viewers.StyledString.DECORATIONS_STYLER;
+import static org.eclipse.jface.viewers.StyledString.QUALIFIER_STYLER;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
+import com.android.resources.ResourceType;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.w3c.dom.Attr;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+class ExtractStyleWizard extends VisualRefactoringWizard {
+ public ExtractStyleWizard(ExtractStyleRefactoring ref, LayoutEditor editor) {
+ super(ref, editor);
+ setDefaultPageTitle(ref.getName());
+ }
+
+ @Override
+ protected void addUserInputPages() {
+ String initialName = "styleName";
+ addPage(new InputPage(mEditor.getProject(), initialName));
+ }
+
+ /**
+ * Wizard page which inputs parameters for the {@link ExtractStyleRefactoring}
+ * operation
+ */
+ private static class InputPage extends VisualRefactoringInputPage {
+ private final IProject mProject;
+ private final String mSuggestedName;
+ private Text mNameText;
+ private Table mTable;
+ private Button mRemoveExtracted;
+ private Button mSetStyle;
+ private Button mRemoveAll;
+ private Button mExtend;;
+ private CheckboxTableViewer mCheckedView;
+
+ private String mParentStyle;
+ private Set<Attr> mInSelection;
+ private List<Attr> mAllAttributes;
+ private int mElementCount;
+ private Map<Attr, Integer> mFrequencyCount;
+ private Set<Attr> mShown;
+ private List<Attr> mInitialChecked;
+ private List<Map.Entry<String, List<Attr>>> mRoot;
+ private Map<String, List<Attr>> mAvailableAttributes;
+
+ public InputPage(IProject project, String suggestedName) {
+ super("ExtractStyleInputPage");
+ mProject = project;
+ mSuggestedName = suggestedName;
+ }
+
+ public void createControl(Composite parent) {
+ initialize();
+
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(2, false));
+
+ Label nameLabel = new Label(composite, SWT.NONE);
+ nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ nameLabel.setText("Style Name:");
+
+ mNameText = new Text(composite, SWT.BORDER);
+ mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mNameText.addModifyListener(mModifyValidateListener);
+
+ mRemoveExtracted = new Button(composite, SWT.CHECK);
+ mRemoveExtracted.setSelection(true);
+ mRemoveExtracted.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+ mRemoveExtracted.setText("Remove extracted attributes");
+ mRemoveExtracted.addSelectionListener(mSelectionValidateListener);
+
+ mRemoveAll = new Button(composite, SWT.CHECK);
+ mRemoveAll.setSelection(false);
+ mRemoveAll.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+ mRemoveAll.setText("Remove all extracted attributes regardless of value");
+ mRemoveAll.addSelectionListener(mSelectionValidateListener);
+
+ boolean defaultSetStyle = false;
+ if (mParentStyle != null) {
+ mExtend = new Button(composite, SWT.CHECK);
+ mExtend.setSelection(true);
+ mExtend.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+ mExtend.setText(String.format("Extend %1$s", mParentStyle));
+ mExtend.addSelectionListener(mSelectionValidateListener);
+ defaultSetStyle = true;
+ }
+
+ mSetStyle = new Button(composite, SWT.CHECK);
+ mSetStyle.setSelection(defaultSetStyle);
+ mSetStyle.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+ mSetStyle.setText("Set style attribute on extracted elements");
+ mSetStyle.addSelectionListener(mSelectionValidateListener);
+
+ new Label(composite, SWT.NONE);
+ new Label(composite, SWT.NONE);
+
+ Label tableLabel = new Label(composite, SWT.NONE);
+ tableLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ tableLabel.setText("Choose style attributes to extract:");
+
+ mCheckedView = CheckboxTableViewer.newCheckList(composite, SWT.BORDER
+ | SWT.FULL_SELECTION | SWT.HIDE_SELECTION);
+ mTable = mCheckedView.getTable();
+ mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 2));
+ ((GridData) mTable.getLayoutData()).heightHint = 200;
+
+ mCheckedView.setContentProvider(new ArgumentContentProvider());
+ mCheckedView.setLabelProvider(new ArgumentLabelProvider());
+ mCheckedView.setInput(mRoot);
+ final Object[] initialSelection = mInitialChecked.toArray();
+ mCheckedView.setCheckedElements(initialSelection);
+
+ mCheckedView.addCheckStateListener(new ICheckStateListener() {
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ // Try to disable other elements that conflict with this
+ boolean isChecked = event.getChecked();
+ if (isChecked) {
+ Attr attribute = (Attr) event.getElement();
+ List<Attr> list = mAvailableAttributes.get(attribute.getLocalName());
+ for (Attr other : list) {
+ if (other != attribute && mShown.contains(other)) {
+ mCheckedView.setChecked(other, false);
+ }
+ }
+ }
+
+ validatePage();
+ }
+ });
+
+ // Select All / Deselect All
+ Composite buttonForm = new Composite(composite, SWT.NONE);
+ buttonForm.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
+ rowLayout.marginTop = 0;
+ rowLayout.marginLeft = 0;
+ buttonForm.setLayout(rowLayout);
+ Button checkAllButton = new Button(buttonForm, SWT.FLAT);
+ checkAllButton.setText("Select All");
+ checkAllButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ // Select "all" (but not conflicting settings)
+ mCheckedView.setCheckedElements(initialSelection);
+ }
+ });
+ Button uncheckAllButton = new Button(buttonForm, SWT.FLAT);
+ uncheckAllButton.setText("Deselect All");
+ uncheckAllButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ mCheckedView.setAllChecked(false);
+ }
+ });
+
+ // Initialize UI:
+ if (mSuggestedName != null) {
+ mNameText.setText(mSuggestedName);
+ }
+
+ setControl(composite);
+ validatePage();
+ }
+
+ private void initialize() {
+ ExtractStyleRefactoring ref = (ExtractStyleRefactoring) getRefactoring();
+
+ mElementCount = ref.getElements().size();
+
+ mParentStyle = ref.getParentStyle();
+
+ // Set up data structures needed by the wizard -- to compute the actual
+ // attributes to list in the wizard (there could be multiple attributes
+ // of the same name (on different elements) and we only want to show one, etc.)
+
+ Pair<Map<String, List<Attr>>, Set<Attr>> result = ref.getAvailableAttributes();
+ // List of all available attributes on the selected elements
+ mAvailableAttributes = result.getFirst();
+ // Set of attributes that overlap the text selection, or all attributes if
+ // wizard is invoked from GUI context
+ mInSelection = result.getSecond();
+
+ // The root data structure, which we set as the table root. The content provider
+ // will produce children from it. This is the entry set of a map from
+ // attribute name to list of attribute nodes for that attribute name.
+ mRoot = new ArrayList<Map.Entry<String, List<Attr>>>(
+ mAvailableAttributes.entrySet());
+
+ // Sort the items by attribute name -- the attribute name is the key
+ // in the entry set above.
+ Collections.sort(mRoot, new Comparator<Map.Entry<String, List<Attr>>>() {
+ public int compare(Map.Entry<String, List<Attr>> e1,
+ Map.Entry<String, List<Attr>> e2) {
+ return e1.getKey().compareTo(e2.getKey());
+ }
+ });
+
+ // Set of attributes actually included in the list shown to the user.
+ // (There could be many additional "aliasing" nodes on other elements
+ // with the same name.) Note however that we DO show multiple attribute
+ // occurrences of the same attribute name: precisely one for each unique -value-
+ // of that attribute.
+ mShown = new HashSet<Attr>();
+
+ // The list of initially checked attributes.
+ mInitialChecked = new ArrayList<Attr>();
+
+ // All attributes.
+ mAllAttributes = new ArrayList<Attr>();
+
+ // Frequency count, from attribute to integer. Attributes that do not
+ // appear in the list have frequency 1, not 0.
+ mFrequencyCount = new HashMap<Attr, Integer>();
+
+ for (Map.Entry<String, List<Attr>> entry : mRoot) {
+ // Iterate over all attributes of the same name, and sort them
+ // by value. This will make it easy to list each -unique- value in the
+ // wizard.
+ List<Attr> attrList = entry.getValue();
+ Collections.sort(attrList, new Comparator<Attr>() {
+ public int compare(Attr a1, Attr a2) {
+ return a1.getValue().compareTo(a2.getValue());
+ }
+ });
+
+ // We need to compute a couple of things: the frequency for all identical
+ // values (and stash them in the frequency map), and record the first
+ // attribute with a particular value into the list of attributes to
+ // be shown.
+ Attr prevAttr = null;
+ String prev = null;
+ List<Attr> uniqueValueAttrs = new ArrayList<Attr>();
+ for (Attr attr : attrList) {
+ String value = attr.getValue();
+ if (value.equals(prev)) {
+ Integer count = mFrequencyCount.get(prevAttr);
+ if (count == null) {
+ count = Integer.valueOf(2);
+ } else {
+ count = Integer.valueOf(count.intValue() + 1);
+ }
+ mFrequencyCount.put(prevAttr, count);
+ } else {
+ uniqueValueAttrs.add(attr);
+ prev = value;
+ prevAttr = attr;
+ }
+ }
+
+ // Sort the values by frequency (and for equal frequencies, alphabetically
+ // by value)
+ Collections.sort(uniqueValueAttrs, new Comparator<Attr>() {
+ public int compare(Attr a1, Attr a2) {
+ Integer f1 = mFrequencyCount.get(a1);
+ Integer f2 = mFrequencyCount.get(a2);
+ if (f1 == null) {
+ f1 = Integer.valueOf(1);
+ }
+ if (f2 == null) {
+ f2 = Integer.valueOf(1);
+ }
+ int delta = f2.intValue() - f1.intValue();
+ if (delta != 0) {
+ return delta;
+ } else {
+ return a1.getValue().compareTo(a2.getValue());
+ }
+ }
+ });
+
+ // Add the items in order, and select those attributes that overlap
+ // the selection
+ mAllAttributes.addAll(uniqueValueAttrs);
+ mShown.addAll(uniqueValueAttrs);
+ Attr first = uniqueValueAttrs.get(0);
+ if (mInSelection.contains(first)) {
+ mInitialChecked.add(first);
+ }
+ }
+ }
+
+ @Override
+ protected boolean validatePage() {
+ boolean ok = true;
+
+ String text = mNameText.getText().trim();
+
+ if (text.length() == 0) {
+ setErrorMessage("Provide a name for the new style");
+ ok = false;
+ } else {
+ ResourceNameValidator validator = ResourceNameValidator.create(false, mProject,
+ ResourceType.STYLE);
+ String message = validator.isValid(text);
+ if (message != null) {
+ setErrorMessage(message);
+ ok = false;
+ }
+ }
+
+ Object[] checkedElements = mCheckedView.getCheckedElements();
+ if (checkedElements.length == 0) {
+ setErrorMessage("Choose at least one attribute to extract");
+ ok = false;
+ }
+
+ if (ok) {
+ setErrorMessage(null);
+
+ // Record state
+ ExtractStyleRefactoring refactoring = (ExtractStyleRefactoring) getRefactoring();
+ refactoring.setStyleName(text);
+ refactoring.setRemoveExtracted(mRemoveExtracted.getSelection());
+ refactoring.setRemoveAll(mRemoveAll.getSelection());
+ refactoring.setApplyStyle(mSetStyle.getSelection());
+ if (mExtend != null && mExtend.getSelection()) {
+ refactoring.setParent(mParentStyle);
+ }
+ List<Attr> attributes = new ArrayList<Attr>();
+ for (Object o : checkedElements) {
+ attributes.add((Attr) o);
+ }
+ refactoring.setChosenAttributes(attributes);
+ }
+
+ setPageComplete(ok);
+ return ok;
+ }
+
+ private class ArgumentLabelProvider extends StyledCellLabelProvider {
+ public ArgumentLabelProvider() {
+ }
+
+ @Override
+ public void update(ViewerCell cell) {
+ Object element = cell.getElement();
+ Attr attribute = (Attr) element;
+
+ StyledString styledString = new StyledString();
+ styledString.append(attribute.getLocalName());
+ styledString.append(" = ", QUALIFIER_STYLER);
+ styledString.append(attribute.getValue());
+
+ if (mElementCount > 1) {
+ Integer f = mFrequencyCount.get(attribute);
+ String s = String.format(" (in %d/%d elements)",
+ f != null ? f.intValue(): 1, mElementCount);
+ styledString.append(s, DECORATIONS_STYLER);
+ }
+ cell.setText(styledString.toString());
+ cell.setStyleRanges(styledString.getStyleRanges());
+ super.update(cell);
+ }
+ }
+
+ private class ArgumentContentProvider implements IStructuredContentProvider {
+ public ArgumentContentProvider() {
+ }
+
+ public Object[] getElements(Object inputElement) {
+ if (inputElement == mRoot) {
+ return mAllAttributes.toArray();
+ }
+
+ return new Object[0];
+ }
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java
new file mode 100644
index 0000000..4baad1d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring;
+import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.ui.StructuredTextEditor;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+
+/**
+ * QuickAssistProcessor which helps invoke refactoring operations on text elements.
+ */
+@SuppressWarnings("restriction") // XML model
+public class RefactoringAssistant implements IQuickAssistProcessor {
+
+ public RefactoringAssistant() {
+ }
+
+ public boolean canAssist(IQuickAssistInvocationContext invocationContext) {
+ return true;
+ }
+
+ public boolean canFix(Annotation annotation) {
+ return true;
+ }
+
+ public ICompletionProposal[] computeQuickAssistProposals(
+ IQuickAssistInvocationContext invocationContext) {
+
+ ISourceViewer sourceViewer = invocationContext.getSourceViewer();
+ AndroidXmlEditor xmlEditor = AndroidXmlEditor.getAndroidXmlEditor(sourceViewer);
+ if (xmlEditor == null) {
+ return null;
+ }
+
+ IFile file = xmlEditor.getInputFile();
+ int offset = invocationContext.getOffset();
+
+ // Ensure that we are over a tag name (for element-based refactoring
+ // operations) or a value (for the extract include refactoring)
+
+ boolean isValue = false;
+ boolean isTagName = false;
+ boolean isAttributeName = false;
+ IStructuredModel model = null;
+ try {
+ model = xmlEditor.getModelForRead();
+ IStructuredDocument doc = model.getStructuredDocument();
+ IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(offset);
+ ITextRegion subRegion = region.getRegionAtCharacterOffset(offset);
+ if (subRegion != null) {
+ String type = subRegion.getType();
+ if (type.equals(DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)) {
+ String value = region.getText(subRegion);
+ // Only extract values that aren't already resources
+ // (and value includes leading ' or ")
+ if (!value.startsWith("'@") && !value.startsWith("\"@")) { //$NON-NLS-1$ //$NON-NLS-2$
+ isValue = true;
+ }
+ } else if (type.equals(DOMRegionContext.XML_TAG_NAME)
+ || type.equals(DOMRegionContext.XML_TAG_OPEN)
+ || type.equals(DOMRegionContext.XML_TAG_CLOSE)) {
+ isTagName = true;
+ } else if (type.equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)
+ || type.equals(DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS)) {
+ isAttributeName = true;
+ }
+ }
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+
+ if (isValue || isTagName || isAttributeName) {
+ StructuredTextEditor structuredEditor = xmlEditor.getStructuredTextEditor();
+ ISelectionProvider provider = structuredEditor.getSelectionProvider();
+ ISelection selection = provider.getSelection();
+ if (selection instanceof ITextSelection) {
+ ITextSelection textSelection = (ITextSelection) selection;
+
+ // These operations currently do not work on ranges
+ if (textSelection.getLength() > 0) {
+ // ...except for Extract Style where the actual attributes overlapping
+ // the selection is going to be the set of eligible attributes
+ if (isAttributeName && xmlEditor instanceof LayoutEditor) {
+ LayoutEditor editor = (LayoutEditor) xmlEditor;
+ return new ICompletionProposal[] {
+ new RefactoringProposal(editor,
+ new ExtractStyleRefactoring(file, editor, textSelection, null))
+ };
+ }
+ return null;
+ }
+
+ if (isAttributeName && xmlEditor instanceof LayoutEditor) {
+ LayoutEditor editor = (LayoutEditor) xmlEditor;
+ return new ICompletionProposal[] {
+ new RefactoringProposal(editor,
+ new ExtractStyleRefactoring(file, editor, textSelection, null)),
+ };
+ } else if (isValue) {
+ if (xmlEditor instanceof LayoutEditor) {
+ LayoutEditor editor = (LayoutEditor) xmlEditor;
+ return new ICompletionProposal[] {
+ new RefactoringProposal(xmlEditor,
+ new ExtractStringRefactoring(file, xmlEditor,
+ textSelection)),
+ new RefactoringProposal(editor,
+ new ExtractStyleRefactoring(file, editor,
+ textSelection, null)),
+ };
+ } else {
+ return new ICompletionProposal[] {
+ new RefactoringProposal(xmlEditor,
+ new ExtractStringRefactoring(file, xmlEditor, textSelection))
+ };
+ }
+ } else if (xmlEditor instanceof LayoutEditor) {
+ LayoutEditor editor = (LayoutEditor) xmlEditor;
+ return new ICompletionProposal[] {
+ new RefactoringProposal(editor,
+ new WrapInRefactoring(file, editor, textSelection, null)),
+ new RefactoringProposal(editor,
+ new ChangeViewRefactoring(file, editor, textSelection, null)),
+ new RefactoringProposal(editor,
+ new ChangeLayoutRefactoring(file, editor, textSelection, null)),
+ new RefactoringProposal(editor,
+ new ExtractStyleRefactoring(file, editor, textSelection, null)),
+ new RefactoringProposal(editor,
+ new ExtractIncludeRefactoring(file, editor, textSelection, null)),
+ };
+ }
+ }
+ }
+ return null;
+ }
+
+ public String getErrorMessage() {
+ return null;
+ }
+
+ private static class RefactoringProposal
+ implements ICompletionProposal {
+ private final AndroidXmlEditor mEditor;
+ private final Refactoring mRefactoring;
+
+ RefactoringProposal(AndroidXmlEditor editor, Refactoring refactoring) {
+ super();
+ mEditor = editor;
+ mRefactoring = refactoring;
+ }
+
+ public void apply(IDocument document) {
+ RefactoringWizard wizard = null;
+ if (mRefactoring instanceof VisualRefactoring) {
+ wizard = ((VisualRefactoring) mRefactoring).createWizard();
+ } else if (mRefactoring instanceof ExtractStringRefactoring) {
+ wizard = new ExtractStringWizard((ExtractStringRefactoring) mRefactoring,
+ mEditor.getProject());
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ op.run(window.getShell(), wizard.getDefaultPageTitle());
+ } catch (InterruptedException e) {
+ }
+ }
+
+ public String getAdditionalProposalInfo() {
+ return "Initiates the given refactoring operation";
+ }
+
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ public String getDisplayString() {
+ return mRefactoring.getName();
+ }
+
+ public Image getImage() {
+ return AdtPlugin.getAndroidLogo();
+ }
+
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RelativeLayoutConversionHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RelativeLayoutConversionHelper.java
new file mode 100644
index 0000000..193c3c0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RelativeLayoutConversionHelper.java
@@ -0,0 +1,1668 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_BACKGROUND;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_BASELINE_ALIGNED;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ABOVE;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_BASELINE;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_BOTTOM;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_LEFT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_PARENT_BOTTOM;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_PARENT_TOP;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_RIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_TOP;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_BELOW;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_CENTER_HORIZONTAL;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_CENTER_VERTICAL;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_GRAVITY;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_MARGIN_LEFT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_MARGIN_TOP;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_TO_LEFT_OF;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_TO_RIGHT_OF;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WEIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_BOTTOM;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_CENTER;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_CENTER_HORIZONTAL;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_CENTER_VERTICAL;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_FILL;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_FILL_HORIZONTAL;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_FILL_VERTICAL;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_LEFT;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_RIGHT;
+import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_TOP;
+import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.LINEAR_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.RELATIVE_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_FALSE;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_TRUE;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
+import com.android.util.Pair;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper class which performs the bulk of the layout conversion to relative layout
+ * <p>
+ * Future enhancements:
+ * <ul>
+ * <li>Render the layout at multiple screen sizes and analyze how the widgets move and
+ * stretch and use that to add in additional constraints
+ * <li> Adapt the LinearLayout analysis code to work with TableLayouts and TableRows as well
+ * (just need to tweak the "isVertical" interpretation to account for the different defaults,
+ * and perhaps do something about column size properties.
+ * <li> We need to take into account existing margins and clear/update them
+ * </ul>
+ */
+class RelativeLayoutConversionHelper {
+ private final MultiTextEdit mRootEdit;
+ private final boolean mFlatten;
+ private final Element mLayout;
+ private final ChangeLayoutRefactoring mRefactoring;
+ private final CanvasViewInfo mRootView;
+
+ RelativeLayoutConversionHelper(ChangeLayoutRefactoring refactoring,
+ Element layout, boolean flatten, MultiTextEdit rootEdit, CanvasViewInfo rootView) {
+ mRefactoring = refactoring;
+ mLayout = layout;
+ mFlatten = flatten;
+ mRootEdit = rootEdit;
+ mRootView = rootView;
+ }
+
+ /** Performs conversion from any layout to a RelativeLayout */
+ public void convertToRelative() {
+ // Locate the view for the layout
+ CanvasViewInfo layoutView = findViewForElement(mRootView, mLayout);
+ if (layoutView == null || layoutView.getChildren().size() == 0) {
+ // No children. THAT was an easy conversion!
+ return;
+ }
+
+ // Study the layout and get information about how to place individual elements
+ List<View> views = analyzeLayout(layoutView);
+
+ // Create/update relative layout constraints
+ createAttachments(views);
+ }
+
+ /**
+ * Analyzes the given view hierarchy and produces a list of {@link View} objects which
+ * contain placement information for each element
+ */
+ private List<View> analyzeLayout(CanvasViewInfo layoutView) {
+ EdgeList edgeList = new EdgeList(layoutView);
+ deleteRemovedElements(edgeList.getDeletedElements());
+
+ List<Integer> columnOffsets = edgeList.getColumnOffsets();
+ List<Integer> rowOffsets = edgeList.getRowOffsets();
+
+ // Compute x/y offsets for each row/column index
+ int[] left = new int[columnOffsets.size()];
+ int[] top = new int[rowOffsets.size()];
+
+ Map<Integer, Integer> xToCol = new HashMap<Integer, Integer>();
+ int columnIndex = 0;
+ for (Integer offset : columnOffsets) {
+ left[columnIndex] = offset;
+ xToCol.put(offset, columnIndex++);
+ }
+ Map<Integer, Integer> yToRow = new HashMap<Integer, Integer>();
+ int rowIndex = 0;
+ for (Integer offset : rowOffsets) {
+ top[rowIndex] = offset;
+ yToRow.put(offset, rowIndex++);
+ }
+
+ // Create a complete list of view objects
+ List<View> views = createViews(edgeList, columnOffsets);
+ initializeSpans(edgeList, columnOffsets, rowOffsets, xToCol, yToRow);
+
+ // Sanity check
+ for (View view : views) {
+ assert view.getLeftEdge() == left[view.mCol];
+ assert view.getTopEdge() == top[view.mRow];
+ assert view.getRightEdge() == left[view.mCol+view.mColSpan];
+ assert view.getBottomEdge() == top[view.mRow+view.mRowSpan];
+ }
+
+ // Ensure that every view has a proper id such that it can be referred to
+ // with a constraint
+ initializeIds(edgeList, views);
+
+ // Attempt to lay the views out in a grid with constraints (though not that widgets
+ // can overlap as well)
+ Grid grid = new Grid(views, left, top);
+ computeKnownConstraints(views, edgeList);
+ computeHorizontalConstraints(grid);
+ computeVerticalConstraints(grid);
+
+ return views;
+ }
+
+ /** Produces a list of {@link View} objects from an {@link EdgeList} */
+ private List<View> createViews(EdgeList edgeList, List<Integer> columnOffsets) {
+ List<View> views = new ArrayList<View>();
+ for (Integer offset : columnOffsets) {
+ List<View> leftEdgeViews = edgeList.getLeftEdgeViews(offset);
+ if (leftEdgeViews == null) {
+ // must have been a right edge
+ continue;
+ }
+ for (View view : leftEdgeViews) {
+ views.add(view);
+ }
+ }
+ return views;
+ }
+
+ /** Removes any elements targeted for deletion */
+ private void deleteRemovedElements(List<Element> delete) {
+ if (mFlatten && delete.size() > 0) {
+ for (Element element : delete) {
+ mRefactoring.removeElementTags(mRootEdit, element, delete);
+ }
+ }
+ }
+
+ /** Ensures that every element has an id such that it can be referenced from a constraint */
+ private void initializeIds(EdgeList edgeList, List<View> views) {
+ // Ensure that all views have a valid id
+ for (View view : views) {
+ String id = mRefactoring.ensureHasId(mRootEdit, view.mElement, null);
+ edgeList.setIdAttributeValue(view, id);
+ }
+ }
+
+ /**
+ * Initializes the column and row indices, as well as any column span and row span
+ * values
+ */
+ private void initializeSpans(EdgeList edgeList, List<Integer> columnOffsets,
+ List<Integer> rowOffsets, Map<Integer, Integer> xToCol, Map<Integer, Integer> yToRow) {
+ // Now initialize table view row, column and spans
+ for (Integer offset : columnOffsets) {
+ List<View> leftEdgeViews = edgeList.getLeftEdgeViews(offset);
+ if (leftEdgeViews == null) {
+ // must have been a right edge
+ continue;
+ }
+ for (View view : leftEdgeViews) {
+ Integer col = xToCol.get(view.getLeftEdge());
+ assert col != null;
+ Integer end = xToCol.get(view.getRightEdge());
+ assert end != null;
+
+ view.mCol = col;
+ view.mColSpan = end - col;
+ }
+ }
+
+ for (Integer offset : rowOffsets) {
+ List<View> topEdgeViews = edgeList.getTopEdgeViews(offset);
+ if (topEdgeViews == null) {
+ // must have been a bottom edge
+ continue;
+ }
+ for (View view : topEdgeViews) {
+ Integer row = yToRow.get(view.getTopEdge());
+ assert row != null;
+ Integer end = yToRow.get(view.getBottomEdge());
+ assert end != null;
+
+ view.mRow = row;
+ view.mRowSpan = end - row;
+ }
+ }
+ }
+
+ /**
+ * Creates refactoring edits which adds or updates constraints for the given list of
+ * views
+ */
+ private void createAttachments(List<View> views) {
+ // Make the attachments
+ String namespace = mRefactoring.getAndroidNamespacePrefix();
+ for (View view : views) {
+ for (Pair<String, String> constraint : view.getHorizConstraints()) {
+ mRefactoring.setAttribute(mRootEdit, view.mElement, ANDROID_URI,
+ namespace, constraint.getFirst(), constraint.getSecond());
+ }
+ for (Pair<String, String> constraint : view.getVerticalConstraints()) {
+ mRefactoring.setAttribute(mRootEdit, view.mElement, ANDROID_URI,
+ namespace, constraint.getFirst(), constraint.getSecond());
+ }
+ }
+ }
+
+ /**
+ * Analyzes the existing layouts and layout parameter objects in the document to infer
+ * constraints for layout types that we know about - such as LinearLayout baseline
+ * alignment, weights, gravity, etc.
+ */
+ private void computeKnownConstraints(List<View> views, EdgeList edgeList) {
+ // List of parent layout elements we've already processed. We iterate through all
+ // the -children-, and we ask each for its element parent (which won't have a view)
+ // and we look at the parent's layout attributes and its children layout constraints,
+ // and then we stash away constraints that we can infer. This means that we will
+ // encounter the same parent for every sibling, so that's why there's a map to
+ // prevent duplicate work.
+ Set<Node> seen = new HashSet<Node>();
+
+ for (View view : views) {
+ Element element = view.getElement();
+ Node parent = element.getParentNode();
+ if (seen.contains(parent)) {
+ continue;
+ }
+ seen.add(parent);
+
+ if (parent.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+ Element layout = (Element) parent;
+ String layoutName = layout.getTagName();
+
+ if (LINEAR_LAYOUT.equals(layoutName)) {
+ analyzeLinearLayout(edgeList, layout);
+ } else if (RELATIVE_LAYOUT.equals(layoutName)) {
+ analyzeRelativeLayout(edgeList, layout);
+ } else {
+ // Some other layout -- add more conditional handling here
+ // for framelayout, tables, etc.
+ }
+ }
+ }
+
+ private static final int GRAVITY_LEFT = 1 << 0;
+ private static final int GRAVITY_RIGHT = 1<< 1;
+ private static final int GRAVITY_CENTER_HORIZ = 1 << 2;
+ private static final int GRAVITY_FILL_HORIZ = 1 << 3;
+ private static final int GRAVITY_CENTER_VERT = 1 << 4;
+ private static final int GRAVITY_FILL_VERT = 1 << 5;
+ private static final int GRAVITY_TOP = 1 << 6;
+ private static final int GRAVITY_BOTTOM = 1 << 7;
+ private static final int GRAVITY_HORIZ_MASK = GRAVITY_CENTER_HORIZ | GRAVITY_FILL_HORIZ
+ | GRAVITY_LEFT | GRAVITY_RIGHT;
+ private static final int GRAVITY_VERT_MASK = GRAVITY_CENTER_VERT | GRAVITY_FILL_VERT
+ | GRAVITY_TOP | GRAVITY_BOTTOM;
+
+ /** Returns the gravity of the given element */
+ private static int getGravity(Element element) {
+ int gravity = GRAVITY_LEFT | GRAVITY_TOP;
+ String gravityString = element.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_GRAVITY);
+ if (gravityString != null && gravityString.length() > 0) {
+ String[] anchors = gravityString.split("\\|"); //$NON-NLS-1$
+ for (String anchor : anchors) {
+ if (GRAVITY_VALUE_CENTER.equals(anchor)) {
+ gravity = GRAVITY_CENTER_HORIZ | GRAVITY_CENTER_VERT;
+ } else if (GRAVITY_VALUE_FILL.equals(anchor)) {
+ gravity = GRAVITY_FILL_HORIZ | GRAVITY_FILL_VERT;
+ } else if (GRAVITY_VALUE_CENTER_VERTICAL.equals(anchor)) {
+ gravity = (gravity & GRAVITY_HORIZ_MASK) | GRAVITY_CENTER_VERT;
+ } else if (GRAVITY_VALUE_CENTER_HORIZONTAL.equals(anchor)) {
+ gravity = (gravity & GRAVITY_VERT_MASK) | GRAVITY_CENTER_HORIZ;
+ } else if (GRAVITY_VALUE_FILL_VERTICAL.equals(anchor)) {
+ gravity = (gravity & GRAVITY_HORIZ_MASK) | GRAVITY_FILL_VERT;
+ } else if (GRAVITY_VALUE_FILL_HORIZONTAL.equals(anchor)) {
+ gravity = (gravity & GRAVITY_VERT_MASK) | GRAVITY_FILL_HORIZ;
+ } else if (GRAVITY_VALUE_TOP.equals(anchor)) {
+ gravity = (gravity & GRAVITY_HORIZ_MASK) | GRAVITY_TOP;
+ } else if (GRAVITY_VALUE_BOTTOM.equals(anchor)) {
+ gravity = (gravity & GRAVITY_HORIZ_MASK) | GRAVITY_BOTTOM;
+ } else if (GRAVITY_VALUE_LEFT.equals(anchor)) {
+ gravity = (gravity & GRAVITY_VERT_MASK) | GRAVITY_LEFT;
+ } else if (GRAVITY_VALUE_RIGHT.equals(anchor)) {
+ gravity = (gravity & GRAVITY_VERT_MASK) | GRAVITY_RIGHT;
+ } else {
+ // "clip" not supported
+ }
+ }
+ }
+
+ return gravity;
+ }
+
+ /**
+ * Returns the layout weight of of the given child of a LinearLayout, or 0.0 if it
+ * does not define a weight
+ */
+ private float getWeight(Element linearLayoutChild) {
+ String weight = linearLayoutChild.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT);
+ if (weight != null && weight.length() > 0) {
+ try {
+ return Float.parseFloat(weight);
+ } catch (NumberFormatException nfe) {
+ AdtPlugin.log(nfe, "Invalid weight %1$s", weight);
+ }
+ }
+
+ return 0.0f;
+ }
+
+ /**
+ * Returns the sum of all the layout weights of the children in the given LinearLayout
+ *
+ * @param linearLayout the layout to compute the total sum for
+ * @return the total sum of all the layout weights in the given layout
+ */
+ private float getWeightSum(Element linearLayout) {
+ float sum = 0;
+ for (Element child : DomUtilities.getChildren(linearLayout)) {
+ sum += getWeight(child);
+ }
+
+ return sum;
+ }
+
+ /**
+ * Analyzes the given LinearLayout and updates the constraints to reflect
+ * relationships it can infer - based on baseline alignment, gravity, order and
+ * weights. This method also removes "0dip" as a special width/height used in
+ * LinearLayouts with weight distribution.
+ */
+ private void analyzeLinearLayout(EdgeList edgeList, Element layout) {
+ boolean isVertical = VALUE_VERTICAL.equals(layout.getAttributeNS(ANDROID_URI,
+ ATTR_ORIENTATION));
+ View baselineRef = null;
+ if (!isVertical &&
+ !VALUE_FALSE.equals(layout.getAttributeNS(ANDROID_URI, ATTR_BASELINE_ALIGNED))) {
+ // Baseline alignment. Find the tallest child and set it as the baseline reference.
+ int tallestHeight = 0;
+ View tallest = null;
+ for (Element child : DomUtilities.getChildren(layout)) {
+ View view = edgeList.getView(child);
+ if (view != null && view.getHeight() > tallestHeight) {
+ tallestHeight = view.getHeight();
+ tallest = view;
+ }
+ }
+ if (tallest != null) {
+ baselineRef = tallest;
+ }
+ }
+
+ float weightSum = getWeightSum(layout);
+ float cumulativeWeight = 0;
+
+ List<Element> children = DomUtilities.getChildren(layout);
+ String prevId = null;
+ boolean isFirstChild = true;
+ boolean linkBackwards = true;
+ boolean linkForwards = false;
+
+ for (int index = 0, childCount = children.size(); index < childCount; index++) {
+ Element child = children.get(index);
+
+ View childView = edgeList.getView(child);
+ if (childView == null) {
+ // Could be a nested layout that is being removed etc
+ prevId = null;
+ isFirstChild = false;
+ continue;
+ }
+
+ // Look at the layout_weight attributes and determine whether we should be
+ // attached on the bottom/right or on the top/left
+ if (weightSum > 0.0f) {
+ float weight = getWeight(child);
+
+ // We can't emulate a LinearLayout where multiple children have positive
+ // weights. However, we CAN support the common scenario where a single
+ // child has a non-zero weight, and all children after it are pushed
+ // to the end and the weighted child fills the remaining space.
+ if (cumulativeWeight == 0 && weight > 0) {
+ // See if we have a bottom/right edge to attach the forwards link to
+ // (at the end of the forwards chains). Only if so can we link forwards.
+ View referenced;
+ if (isVertical) {
+ referenced = edgeList.getSharedBottomEdge(layout);
+ } else {
+ referenced = edgeList.getSharedRightEdge(layout);
+ }
+ if (referenced != null) {
+ linkForwards = true;
+ }
+ } else if (cumulativeWeight > 0) {
+ linkBackwards = false;
+ }
+
+ cumulativeWeight += weight;
+ }
+
+ analyzeGravity(edgeList, layout, isVertical, child, childView);
+ convert0dipToWrapContent(child);
+
+ // Chain elements together in the flow direction of the linear layout
+ if (prevId != null) { // No constraint for first child
+ if (linkBackwards) {
+ if (isVertical) {
+ childView.addVerticalConstraint(ATTR_LAYOUT_BELOW, prevId);
+ } else {
+ childView.addHorizConstraint(ATTR_LAYOUT_TO_RIGHT_OF, prevId);
+ }
+ }
+ } else if (isFirstChild) {
+ assert linkBackwards;
+
+ // First element; attach it to the parent if we can
+ if (isVertical) {
+ View referenced = edgeList.getSharedTopEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_TOP,
+ VALUE_TRUE);
+ } else {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP,
+ referenced.getId());
+ }
+ }
+ } else {
+ View referenced = edgeList.getSharedLeftEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_LEFT,
+ VALUE_TRUE);
+ } else {
+ childView.addHorizConstraint(
+ ATTR_LAYOUT_ALIGN_LEFT, referenced.getId());
+ }
+ }
+ }
+ }
+
+ if (linkForwards) {
+ if (index < (childCount - 1)) {
+ Element nextChild = children.get(index + 1);
+ String nextId = mRefactoring.ensureHasId(mRootEdit, nextChild, null);
+ if (nextId != null) {
+ if (isVertical) {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ABOVE, nextId);
+ } else {
+ childView.addHorizConstraint(ATTR_LAYOUT_TO_LEFT_OF, nextId);
+ }
+ }
+ } else {
+ // Attach to right/bottom edge of the layout
+ if (isVertical) {
+ View referenced = edgeList.getSharedBottomEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM,
+ VALUE_TRUE);
+ } else {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_BOTTOM,
+ referenced.getId());
+ }
+ }
+ } else {
+ View referenced = edgeList.getSharedRightEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_RIGHT,
+ VALUE_TRUE);
+ } else {
+ childView.addHorizConstraint(
+ ATTR_LAYOUT_ALIGN_RIGHT, referenced.getId());
+ }
+ }
+ }
+ }
+ }
+
+ if (baselineRef != null && !baselineRef.equals(childView.getId())) {
+ assert !isVertical;
+ // Only align if they share the same gravity
+ if ((childView.getGravity() & GRAVITY_VERT_MASK) ==
+ (baselineRef.getGravity() & GRAVITY_VERT_MASK)) {
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_BASELINE, baselineRef.getId());
+ }
+ }
+
+ prevId = mRefactoring.ensureHasId(mRootEdit, child, null);
+ isFirstChild = false;
+ }
+ }
+
+ /**
+ * Checks the layout "gravity" value for the given child and updates the constraints
+ * to account for the gravity
+ */
+ private int analyzeGravity(EdgeList edgeList, Element layout, boolean isVertical,
+ Element child, View childView) {
+ // Use gravity to constrain elements in the axis orthogonal to the
+ // direction of the layout
+ int gravity = childView.getGravity();
+ if (isVertical) {
+ if ((gravity & GRAVITY_RIGHT) != 0) {
+ View referenced = edgeList.getSharedRightEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_RIGHT,
+ VALUE_TRUE);
+ } else {
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_RIGHT,
+ referenced.getId());
+ }
+ }
+ } else if ((gravity & GRAVITY_CENTER_HORIZ) != 0) {
+ View referenced1 = edgeList.getSharedLeftEdge(layout);
+ View referenced2 = edgeList.getSharedRightEdge(layout);
+ if (referenced1 != null && referenced2 == referenced1) {
+ if (isAncestor(referenced1.getElement(), child)) {
+ childView.addHorizConstraint(ATTR_LAYOUT_CENTER_HORIZONTAL,
+ VALUE_TRUE);
+ }
+ }
+ } else if ((gravity & GRAVITY_FILL_HORIZ) != 0) {
+ View referenced1 = edgeList.getSharedLeftEdge(layout);
+ View referenced2 = edgeList.getSharedRightEdge(layout);
+ if (referenced1 != null && referenced2 == referenced1) {
+ if (isAncestor(referenced1.getElement(), child)) {
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_LEFT,
+ VALUE_TRUE);
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_RIGHT,
+ VALUE_TRUE);
+ } else {
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_LEFT,
+ referenced1.getId());
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_RIGHT,
+ referenced2.getId());
+ }
+ }
+ } else if ((gravity & GRAVITY_LEFT) != 0) {
+ View referenced = edgeList.getSharedLeftEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_LEFT,
+ VALUE_TRUE);
+ } else {
+ childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_LEFT,
+ referenced.getId());
+ }
+ }
+ }
+ } else {
+ // Handle horizontal layout: perform vertical gravity attachments
+ if ((gravity & GRAVITY_BOTTOM) != 0) {
+ View referenced = edgeList.getSharedBottomEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM,
+ VALUE_TRUE);
+ } else {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_BOTTOM,
+ referenced.getId());
+ }
+ }
+ } else if ((gravity & GRAVITY_CENTER_VERT) != 0) {
+ View referenced1 = edgeList.getSharedTopEdge(layout);
+ View referenced2 = edgeList.getSharedBottomEdge(layout);
+ if (referenced1 != null && referenced2 == referenced1) {
+ if (isAncestor(referenced1.getElement(), child)) {
+ childView.addVerticalConstraint(ATTR_LAYOUT_CENTER_VERTICAL,
+ VALUE_TRUE);
+ }
+ }
+ } else if ((gravity & GRAVITY_FILL_VERT) != 0) {
+ View referenced1 = edgeList.getSharedTopEdge(layout);
+ View referenced2 = edgeList.getSharedBottomEdge(layout);
+ if (referenced1 != null && referenced2 == referenced1) {
+ if (isAncestor(referenced1.getElement(), child)) {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_TOP,
+ VALUE_TRUE);
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM,
+ VALUE_TRUE);
+ } else {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP,
+ referenced1.getId());
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_BOTTOM,
+ referenced2.getId());
+ }
+ }
+ } else if ((gravity & GRAVITY_TOP) != 0) {
+ View referenced = edgeList.getSharedTopEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_TOP,
+ VALUE_TRUE);
+ } else {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP,
+ referenced.getId());
+ }
+ }
+ }
+ }
+ return gravity;
+ }
+
+ /** Converts 0dip values in layout_width and layout_height to wrap_content instead */
+ private void convert0dipToWrapContent(Element child) {
+ // Must convert layout_height="0dip" to layout_height="wrap_content".
+ // 0dip is a special trick used in linear layouts in the presence of
+ // weights where 0dip ensures that the height of the view is not taken
+ // into account when distributing the weights. However, when converted
+ // to RelativeLayout this will instead cause the view to actually be assigned
+ // 0 height.
+ String height = child.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT);
+ // 0dip, 0dp, 0px, etc
+ if (height != null && height.startsWith("0")) { //$NON-NLS-1$
+ mRefactoring.setAttribute(mRootEdit, child, ANDROID_URI,
+ mRefactoring.getAndroidNamespacePrefix(), ATTR_LAYOUT_HEIGHT,
+ VALUE_WRAP_CONTENT);
+ }
+ String width = child.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH);
+ if (width != null && width.startsWith("0")) { //$NON-NLS-1$
+ mRefactoring.setAttribute(mRootEdit, child, ANDROID_URI,
+ mRefactoring.getAndroidNamespacePrefix(), ATTR_LAYOUT_WIDTH,
+ VALUE_WRAP_CONTENT);
+ }
+ }
+
+ /**
+ * Analyzes an embedded RelativeLayout within a layout hierarchy and updates the
+ * constraints in the EdgeList with those relationships which can continue in the
+ * outer single RelativeLayout.
+ */
+ private void analyzeRelativeLayout(EdgeList edgeList, Element layout) {
+ NodeList children = layout.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element child = (Element) node;
+ View childView = edgeList.getView(child);
+ if (childView == null) {
+ // Could be a nested layout that is being removed etc
+ continue;
+ }
+
+ NamedNodeMap attributes = child.getAttributes();
+ for (int j = 0, m = attributes.getLength(); j < m; j++) {
+ Attr attribute = (Attr) attributes.item(j);
+ String name = attribute.getLocalName();
+ String value = attribute.getValue();
+ if (name.equals(ATTR_LAYOUT_WIDTH)
+ || name.equals(ATTR_LAYOUT_HEIGHT)) {
+ // Ignore these for now
+ } else if (name.startsWith(ATTR_LAYOUT_PREFIX)
+ && ANDROID_URI.equals(attribute.getNamespaceURI())) {
+ // Determine if the reference is to a known edge
+ String id = getIdBasename(value);
+ if (id != null) {
+ View referenced = edgeList.getView(id);
+ if (referenced != null) {
+ // This is a valid reference, so preserve
+ // the attribute
+ if (name.equals(ATTR_LAYOUT_BELOW) ||
+ name.equals(ATTR_LAYOUT_ABOVE) ||
+ name.equals(ATTR_LAYOUT_ALIGN_TOP) ||
+ name.equals(ATTR_LAYOUT_ALIGN_BOTTOM) ||
+ name.equals(ATTR_LAYOUT_ALIGN_BASELINE)) {
+ // Vertical constraint
+ childView.addVerticalConstraint(name, value);
+ } else if (name.equals(ATTR_LAYOUT_ALIGN_LEFT) ||
+ name.equals(ATTR_LAYOUT_TO_LEFT_OF) ||
+ name.equals(ATTR_LAYOUT_TO_RIGHT_OF) ||
+ name.equals(ATTR_LAYOUT_ALIGN_RIGHT)) {
+ // Horizontal constraint
+ childView.addHorizConstraint(name, value);
+ } else {
+ // We don't expect this
+ assert false : name;
+ }
+ } else {
+ // Reference to some layout that is not included here.
+ // TODO: See if the given layout has an edge
+ // that corresponds to one of our known views
+ // so we can adjust the constraints and keep it after all.
+ }
+ } else {
+ // It's a parent-relative constraint (such
+ // as aligning with a parent edge, or centering
+ // in the parent view)
+ boolean remove = true;
+ if (name.equals(ATTR_LAYOUT_ALIGN_PARENT_LEFT)) {
+ View referenced = edgeList.getSharedLeftEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addHorizConstraint(name, VALUE_TRUE);
+ } else {
+ childView.addHorizConstraint(
+ ATTR_LAYOUT_ALIGN_LEFT, referenced.getId());
+ }
+ remove = false;
+ }
+ } else if (name.equals(ATTR_LAYOUT_ALIGN_PARENT_RIGHT)) {
+ View referenced = edgeList.getSharedRightEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addHorizConstraint(name, VALUE_TRUE);
+ } else {
+ childView.addHorizConstraint(
+ ATTR_LAYOUT_ALIGN_RIGHT, referenced.getId());
+ }
+ remove = false;
+ }
+ } else if (name.equals(ATTR_LAYOUT_ALIGN_PARENT_TOP)) {
+ View referenced = edgeList.getSharedTopEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addVerticalConstraint(name, VALUE_TRUE);
+ } else {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP,
+ referenced.getId());
+ }
+ remove = false;
+ }
+ } else if (name.equals(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM)) {
+ View referenced = edgeList.getSharedBottomEdge(layout);
+ if (referenced != null) {
+ if (isAncestor(referenced.getElement(), child)) {
+ childView.addVerticalConstraint(name, VALUE_TRUE);
+ } else {
+ childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_BOTTOM,
+ referenced.getId());
+ }
+ remove = false;
+ }
+ }
+
+ boolean alignWithParent =
+ name.equals(ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING);
+ if (remove && alignWithParent) {
+ // TODO - look for this one AFTER we have processed
+ // everything else, and then set constraints as necessary
+ // IF there are no other conflicting constraints!
+ }
+
+ // Otherwise it's some kind of centering which we don't support
+ // yet.
+
+ // TODO: Find a way to determine whether we have
+ // a corresponding edge for the parent (e.g. if
+ // the ViewInfo bounds match our outer parent or
+ // some other edge) and if so, substitute for that
+ // id.
+ // For example, if this element was centered
+ // horizontally in a RelativeLayout that actually
+ // occupies the entire width of our outer layout,
+ // then it can be preserved after all!
+
+ if (remove) {
+ if (name.startsWith("layout_margin")) { //$NON-NLS-1$
+ continue;
+ }
+
+ // Remove unknown attributes?
+ // It's too early to do this, because we may later want
+ // to *set* this value and it would result in an overlapping edits
+ // exception. Therefore, we need to RECORD which attributes should
+ // be removed, which lines should have its indentation adjusted
+ // etc and finally process it all at the end!
+ //mRefactoring.removeAttribute(mRootEdit, child,
+ // attribute.getNamespaceURI(), name);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Given {@code @id/foo} or {@code @+id/foo}, returns foo. Note that given foo it will
+ * return null.
+ */
+ private static String getIdBasename(String id) {
+ if (id.startsWith(NEW_ID_PREFIX)) {
+ return id.substring(NEW_ID_PREFIX.length());
+ } else if (id.startsWith(ID_PREFIX)) {
+ return id.substring(ID_PREFIX.length());
+ }
+
+ return null;
+ }
+
+ /** Returns true if the given second argument is a descendant of the first argument */
+ private static boolean isAncestor(Node ancestor, Node node) {
+ while (node != null) {
+ if (node == ancestor) {
+ return true;
+ }
+ node = node.getParentNode();
+ }
+ return false;
+ }
+
+ /**
+ * Computes horizontal constraints for the views in the grid for any remaining views
+ * that do not have constraints (as the result of the analysis of known layouts). This
+ * will look at the rendered layout coordinates and attempt to connect elements based
+ * on a spatial layout in the grid.
+ */
+ private void computeHorizontalConstraints(Grid grid) {
+ int columns = grid.getColumns();
+
+ String attachLeftProperty = ATTR_LAYOUT_ALIGN_PARENT_LEFT;
+ String attachLeftValue = VALUE_TRUE;
+ int marginLeft = 0;
+ for (int col = 0; col < columns; col++) {
+ if (!grid.colContainsTopLeftCorner(col)) {
+ // Just accumulate margins for the next column
+ marginLeft += grid.getColumnWidth(col);
+ } else {
+ // Add horizontal attachments
+ String firstId = null;
+ for (View view : grid.viewsStartingInCol(col, true)) {
+ assert view.getId() != null;
+ if (firstId == null) {
+ firstId = view.getId();
+ if (view.isConstrainedHorizontally()) {
+ // Nothing to do -- we already have an accurate position for
+ // this view
+ } else if (attachLeftProperty != null) {
+ view.addHorizConstraint(attachLeftProperty, attachLeftValue);
+ if (marginLeft > 0) {
+ view.addHorizConstraint(ATTR_LAYOUT_MARGIN_LEFT,
+ String.format(VALUE_N_DP, marginLeft));
+ marginLeft = 0;
+ }
+ } else {
+ assert false;
+ }
+ } else if (!view.isConstrainedHorizontally()) {
+ view.addHorizConstraint(ATTR_LAYOUT_ALIGN_LEFT, firstId);
+ }
+ }
+ }
+
+ // Figure out edge for the next column
+ View view = grid.findRightEdgeView(col);
+ if (view != null) {
+ assert view.getId() != null;
+ attachLeftProperty = ATTR_LAYOUT_TO_RIGHT_OF;
+ attachLeftValue = view.getId();
+
+ marginLeft = 0;
+ } else if (marginLeft == 0) {
+ marginLeft = grid.getColumnWidth(col);
+ }
+ }
+ }
+
+ /**
+ * Performs vertical layout just like the {@link #computeHorizontalConstraints} method
+ * did horizontally
+ */
+ private void computeVerticalConstraints(Grid grid) {
+ int rows = grid.getRows();
+
+ String attachTopProperty = ATTR_LAYOUT_ALIGN_PARENT_TOP;
+ String attachTopValue = VALUE_TRUE;
+ int marginTop = 0;
+ for (int row = 0; row < rows; row++) {
+ if (!grid.rowContainsTopLeftCorner(row)) {
+ // Just accumulate margins for the next column
+ marginTop += grid.getRowHeight(row);
+ } else {
+ // Add horizontal attachments
+ String firstId = null;
+ for (View view : grid.viewsStartingInRow(row, true)) {
+ assert view.getId() != null;
+ if (firstId == null) {
+ firstId = view.getId();
+ if (view.isConstrainedVertically()) {
+ // Nothing to do -- we already have an accurate position for
+ // this view
+ } else if (attachTopProperty != null) {
+ view.addVerticalConstraint(attachTopProperty, attachTopValue);
+ if (marginTop > 0) {
+ view.addVerticalConstraint(ATTR_LAYOUT_MARGIN_TOP,
+ String.format(VALUE_N_DP, marginTop));
+ marginTop = 0;
+ }
+ } else {
+ assert false;
+ }
+ } else if (!view.isConstrainedVertically()) {
+ view.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP, firstId);
+ }
+ }
+ }
+
+ // Figure out edge for the next row
+ View view = grid.findBottomEdgeView(row);
+ if (view != null) {
+ assert view.getId() != null;
+ attachTopProperty = ATTR_LAYOUT_BELOW;
+ attachTopValue = view.getId();
+ marginTop = 0;
+ } else if (marginTop == 0) {
+ marginTop = grid.getRowHeight(row);
+ }
+ }
+ }
+
+ /**
+ * Searches a view hierarchy and locates the {@link CanvasViewInfo} for the given
+ * {@link Element}
+ *
+ * @param info the root {@link CanvasViewInfo} to search below
+ * @param element the target element
+ * @return the {@link CanvasViewInfo} which corresponds to the given element
+ */
+ private CanvasViewInfo findViewForElement(CanvasViewInfo info, Element element) {
+ if (getElement(info) == element) {
+ return info;
+ }
+
+ for (CanvasViewInfo child : info.getChildren()) {
+ CanvasViewInfo result = findViewForElement(child, element);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ /** Returns the {@link Element} for the given {@link CanvasViewInfo} */
+ private static Element getElement(CanvasViewInfo info) {
+ Node node = info.getUiViewNode().getXmlNode();
+ if (node instanceof Element) {
+ return (Element) node;
+ }
+
+ return null;
+ }
+
+ /**
+ * A grid of cells which can contain views, used to infer spatial relationships when
+ * computing constraints. Note that a view can appear in than one cell; they will
+ * appear in all cells that their bounds overlap with!
+ */
+ private class Grid {
+ private final int[] mLeft;
+ private final int[] mTop;
+ // A list from row to column to cell, where a cell is a list of views
+ private final List<List<List<View>>> mRowList;
+ private int mRowCount;
+ private int mColCount;
+
+ Grid(List<View> views, int[] left, int[] top) {
+ mLeft = left;
+ mTop = top;
+
+ // The left/top arrays should include the ending point too
+ mColCount = left.length - 1;
+ mRowCount = top.length - 1;
+
+ // Using nested lists rather than arrays to avoid lack of typed arrays
+ // (can't create List<View>[row][column] arrays)
+ mRowList = new ArrayList<List<List<View>>>(top.length);
+ for (int row = 0; row < top.length; row++) {
+ List<List<View>> columnList = new ArrayList<List<View>>(left.length);
+ for (int col = 0; col < left.length; col++) {
+ columnList.add(new ArrayList<View>(4));
+ }
+ mRowList.add(columnList);
+ }
+
+ for (View view : views) {
+ // Get rid of the root view; we don't want that in the attachments logic;
+ // it was there originally such that it would contribute the outermost
+ // edges.
+ if (view.mElement == mLayout) {
+ continue;
+ }
+
+ for (int i = 0; i < view.mRowSpan; i++) {
+ for (int j = 0; j < view.mColSpan; j++) {
+ mRowList.get(view.mRow + i).get(view.mCol + j).add(view);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the number of rows in the grid
+ *
+ * @return the row count
+ */
+ public int getRows() {
+ return mRowCount;
+ }
+
+ /**
+ * Returns the number of columns in the grid
+ *
+ * @return the column count
+ */
+ public int getColumns() {
+ return mColCount;
+ }
+
+ /**
+ * Returns the list of views overlapping the given cell
+ *
+ * @param row the row of the target cell
+ * @param col the column of the target cell
+ * @return a list of views overlapping the given column
+ */
+ public List<View> get(int row, int col) {
+ return mRowList.get(row).get(col);
+ }
+
+ /**
+ * Returns true if the given column contains a top left corner of a view
+ *
+ * @param column the column to check
+ * @return true if one or more views have their top left corner in this column
+ */
+ public boolean colContainsTopLeftCorner(int column) {
+ for (int row = 0; row < mRowCount; row++) {
+ View view = getTopLeftCorner(row, column);
+ if (view != null) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the given row contains a top left corner of a view
+ *
+ * @param row the row to check
+ * @return true if one or more views have their top left corner in this row
+ */
+ public boolean rowContainsTopLeftCorner(int row) {
+ for (int col = 0; col < mColCount; col++) {
+ View view = getTopLeftCorner(row, col);
+ if (view != null) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a list of views (optionally sorted by increasing row index) that have
+ * their left edge starting in the given column
+ *
+ * @param col the column to look up views for
+ * @param sort whether to sort the result in increasing row order
+ * @return a list of views starting in the given column
+ */
+ public List<View> viewsStartingInCol(int col, boolean sort) {
+ List<View> views = new ArrayList<View>();
+ for (int row = 0; row < mRowCount; row++) {
+ View view = getTopLeftCorner(row, col);
+ if (view != null) {
+ views.add(view);
+ }
+ }
+
+ if (sort) {
+ View.sortByRow(views);
+ }
+
+ return views;
+ }
+
+ /**
+ * Returns a list of views (optionally sorted by increasing column index) that have
+ * their top edge starting in the given row
+ *
+ * @param row the row to look up views for
+ * @param sort whether to sort the result in increasing column order
+ * @return a list of views starting in the given row
+ */
+ public List<View> viewsStartingInRow(int row, boolean sort) {
+ List<View> views = new ArrayList<View>();
+ for (int col = 0; col < mColCount; col++) {
+ View view = getTopLeftCorner(row, col);
+ if (view != null) {
+ views.add(view);
+ }
+ }
+
+ if (sort) {
+ View.sortByColumn(views);
+ }
+
+ return views;
+ }
+
+ /**
+ * Returns the pixel width of the given column
+ *
+ * @param col the column to look up the width of
+ * @return the width of the column
+ */
+ public int getColumnWidth(int col) {
+ return mLeft[col + 1] - mLeft[col];
+ }
+
+ /**
+ * Returns the pixel height of the given row
+ *
+ * @param row the row to look up the height of
+ * @return the height of the row
+ */
+ public int getRowHeight(int row) {
+ return mTop[row + 1] - mTop[row];
+ }
+
+ /**
+ * Returns the first view found that has its top left corner in the cell given by
+ * the row and column indexes, or null if not found.
+ *
+ * @param row the row of the target cell
+ * @param col the column of the target cell
+ * @return a view with its top left corner in the given cell, or null if not found
+ */
+ View getTopLeftCorner(int row, int col) {
+ List<View> views = get(row, col);
+ if (views.size() > 0) {
+ for (View view : views) {
+ if (view.mRow == row && view.mCol == col) {
+ return view;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public View findRightEdgeView(int col) {
+ for (int row = 0; row < mRowCount; row++) {
+ List<View> views = get(row, col);
+ if (views.size() > 0) {
+ List<View> result = new ArrayList<View>();
+ for (View view : views) {
+ // Ends on the right edge of this column?
+ if (view.mCol + view.mColSpan == col + 1) {
+ result.add(view);
+ }
+ }
+ if (result.size() > 1) {
+ View.sortByColumn(result);
+ }
+ if (result.size() > 0) {
+ return result.get(0);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public View findBottomEdgeView(int row) {
+ for (int col = 0; col < mColCount; col++) {
+ List<View> views = get(row, col);
+ if (views.size() > 0) {
+ List<View> result = new ArrayList<View>();
+ for (View view : views) {
+ // Ends on the bottom edge of this column?
+ if (view.mRow + view.mRowSpan == row + 1) {
+ result.add(view);
+ }
+ }
+ if (result.size() > 1) {
+ View.sortByRow(result);
+ }
+ if (result.size() > 0) {
+ return result.get(0);
+ }
+
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Produces a display of view contents along with the pixel positions of each row/column,
+ * like the following (used for diagnostics only)
+ * <pre>
+ * |0 |49 |143 |192 |240
+ * 36| | |button2 |
+ * 72| |radioButton1 |button2 |
+ * 74|button1 |radioButton1 |button2 |
+ * 108|button1 | |button2 |
+ * 110| | |button2 |
+ * 149| | | |
+ * 320
+ * </pre>
+ */
+ @Override
+ public String toString() {
+ // Dump out the view table
+ int cellWidth = 20;
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter out = new PrintWriter(stringWriter);
+ out.printf("%" + cellWidth + "s", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ for (int col = 0; col < mColCount + 1; col++) {
+ out.printf("|%-" + (cellWidth - 1) + "d", mLeft[col]); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ out.printf("\n"); //$NON-NLS-1$
+ for (int row = 0; row < mRowCount + 1; row++) {
+ out.printf("%" + cellWidth + "d", mTop[row]); //$NON-NLS-1$ //$NON-NLS-2$
+ if (row == mRowCount) {
+ break;
+ }
+ for (int col = 0; col < mColCount; col++) {
+ List<View> views = get(row, col);
+ StringBuilder sb = new StringBuilder();
+ for (View view : views) {
+ String id = view != null ? view.getId() : ""; //$NON-NLS-1$
+ if (id.startsWith(NEW_ID_PREFIX)) {
+ id = id.substring(NEW_ID_PREFIX.length());
+ }
+ if (id.length() > cellWidth - 2) {
+ id = id.substring(0, cellWidth - 2);
+ }
+ if (sb.length() > 0) {
+ sb.append(","); //$NON-NLS-1$
+ }
+ sb.append(id);
+ }
+ String cellString = sb.toString();
+ if (cellString.contains(",") && cellString.length() > cellWidth - 2) { //$NON-NLS-1$
+ cellString = cellString.substring(0, cellWidth - 6) + "...,"; //$NON-NLS-1$
+ }
+ out.printf("|%-" + (cellWidth - 2) + "s ", cellString); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ out.printf("\n"); //$NON-NLS-1$
+ }
+
+ out.flush();
+ return stringWriter.toString();
+ }
+ }
+
+ /** Holds layout information about an individual view. */
+ private static class View {
+ private final Element mElement;
+ private int mRow = -1;
+ private int mCol = -1;
+ private int mRowSpan = -1;
+ private int mColSpan = -1;
+ private CanvasViewInfo mInfo;
+ private String mId;
+ private List<Pair<String, String>> mHorizConstraints =
+ new ArrayList<Pair<String, String>>(4);
+ private List<Pair<String, String>> mVerticalConstraints =
+ new ArrayList<Pair<String, String>>(4);
+ private int mGravity;
+
+ public View(CanvasViewInfo view, Element element) {
+ mInfo = view;
+ mElement = element;
+ mGravity = RelativeLayoutConversionHelper.getGravity(element);
+ }
+
+ public int getHeight() {
+ return mInfo.getAbsRect().height;
+ }
+
+ public int getGravity() {
+ return mGravity;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public Element getElement() {
+ return mElement;
+ }
+
+ public List<Pair<String, String>> getHorizConstraints() {
+ return mHorizConstraints;
+ }
+
+ public List<Pair<String, String>> getVerticalConstraints() {
+ return mVerticalConstraints;
+ }
+
+ public boolean isConstrainedHorizontally() {
+ return mHorizConstraints.size() > 0;
+ }
+
+ public boolean isConstrainedVertically() {
+ return mVerticalConstraints.size() > 0;
+ }
+
+ public void addHorizConstraint(String property, String value) {
+ assert property != null && value != null;
+ // TODO - look for duplicates?
+ mHorizConstraints.add(Pair.of(property, value));
+ }
+
+ public void addVerticalConstraint(String property, String value) {
+ assert property != null && value != null;
+ mVerticalConstraints.add(Pair.of(property, value));
+ }
+
+ public int getLeftEdge() {
+ return mInfo.getAbsRect().x;
+ }
+
+ public int getTopEdge() {
+ return mInfo.getAbsRect().y;
+ }
+
+ public int getRightEdge() {
+ Rectangle bounds = mInfo.getAbsRect();
+ // +1: make the bounds overlap, so the right edge is the same as the
+ // left edge of the neighbor etc. Otherwise we end up with lots of 1-pixel wide
+ // columns between adjacent items.
+ return bounds.x + bounds.width + 1;
+ }
+
+ public int getBottomEdge() {
+ Rectangle bounds = mInfo.getAbsRect();
+ return bounds.y + bounds.height + 1;
+ }
+
+ @Override
+ public String toString() {
+ return "View [mId=" + mId + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public static void sortByRow(List<View> views) {
+ Collections.sort(views, new ViewComparator(true/*rowSort*/));
+ }
+
+ public static void sortByColumn(List<View> views) {
+ Collections.sort(views, new ViewComparator(false/*rowSort*/));
+ }
+
+ /** Comparator to help sort views by row or column index */
+ private static class ViewComparator implements Comparator<View> {
+ boolean mRowSort;
+
+ public ViewComparator(boolean rowSort) {
+ mRowSort = rowSort;
+ }
+
+ public int compare(View view1, View view2) {
+ if (mRowSort) {
+ return view1.mRow - view2.mRow;
+ } else {
+ return view1.mCol - view2.mCol;
+ }
+ }
+ }
+ }
+
+ /**
+ * An edge list takes a hierarchy of elements and records the bounds of each element
+ * into various lists such that it can answer queries about shared edges, about which
+ * particular pixels occur as a boundary edge, etc.
+ */
+ private class EdgeList {
+ private final Map<Element, View> mElementToViewMap = new HashMap<Element, View>(100);
+ private final Map<String, View> mIdToViewMap = new HashMap<String, View>(100);
+ private final Map<Integer, List<View>> mLeft = new HashMap<Integer, List<View>>();
+ private final Map<Integer, List<View>> mTop = new HashMap<Integer, List<View>>();
+ private final Map<Integer, List<View>> mRight = new HashMap<Integer, List<View>>();
+ private final Map<Integer, List<View>> mBottom = new HashMap<Integer, List<View>>();
+ private final Map<Element, Element> mSharedLeftEdge = new HashMap<Element, Element>();
+ private final Map<Element, Element> mSharedTopEdge = new HashMap<Element, Element>();
+ private final Map<Element, Element> mSharedRightEdge = new HashMap<Element, Element>();
+ private final Map<Element, Element> mSharedBottomEdge = new HashMap<Element, Element>();
+ private final List<Element> mDelete = new ArrayList<Element>();
+
+ EdgeList(CanvasViewInfo view) {
+ analyze(view, true);
+ mDelete.remove(getElement(view));
+ }
+
+ public void setIdAttributeValue(View view, String id) {
+ assert id.startsWith(NEW_ID_PREFIX) || id.startsWith(ID_PREFIX);
+ view.mId = id;
+ mIdToViewMap.put(getIdBasename(id), view);
+ }
+
+ public View getView(Element element) {
+ return mElementToViewMap.get(element);
+ }
+
+ public View getView(String id) {
+ return mIdToViewMap.get(id);
+ }
+
+ public List<View> getTopEdgeViews(Integer topOffset) {
+ return mTop.get(topOffset);
+ }
+
+ public List<View> getLeftEdgeViews(Integer leftOffset) {
+ return mLeft.get(leftOffset);
+ }
+
+ void record(Map<Integer, List<View>> map, Integer edge, View info) {
+ List<View> list = map.get(edge);
+ if (list == null) {
+ list = new ArrayList<View>();
+ map.put(edge, list);
+ }
+ list.add(info);
+ }
+
+ private List<Integer> getOffsets(Set<Integer> first, Set<Integer> second) {
+ Set<Integer> joined = new HashSet<Integer>(first.size() + second.size());
+ joined.addAll(first);
+ joined.addAll(second);
+ List<Integer> unique = new ArrayList<Integer>(joined);
+ Collections.sort(unique);
+
+ return unique;
+ }
+
+ public List<Element> getDeletedElements() {
+ return mDelete;
+ }
+
+ public List<Integer> getColumnOffsets() {
+ return getOffsets(mLeft.keySet(), mRight.keySet());
+ }
+ public List<Integer> getRowOffsets() {
+ return getOffsets(mTop.keySet(), mBottom.keySet());
+ }
+
+ private View analyze(CanvasViewInfo view, boolean isRoot) {
+ View added = null;
+ if (!mFlatten || !isRemovableLayout(view)) {
+ added = add(view);
+ if (!isRoot) {
+ return added;
+ }
+ } else {
+ mDelete.add(getElement(view));
+ }
+
+ Element parentElement = getElement(view);
+ Rectangle parentBounds = view.getAbsRect();
+
+ // Build up a table model of the view
+ for (CanvasViewInfo child : view.getChildren()) {
+ Rectangle childBounds = child.getAbsRect();
+ Element childElement = getElement(child);
+
+ // See if this view shares the edge with the removed
+ // parent layout, and if so, record that such that we can
+ // later handle attachments to the removed parent edges
+ if (parentBounds.x == childBounds.x) {
+ mSharedLeftEdge.put(childElement, parentElement);
+ }
+ if (parentBounds.y == childBounds.y) {
+ mSharedTopEdge.put(childElement, parentElement);
+ }
+ if (parentBounds.x + parentBounds.width == childBounds.x + childBounds.width) {
+ mSharedRightEdge.put(childElement, parentElement);
+ }
+ if (parentBounds.y + parentBounds.height == childBounds.y + childBounds.height) {
+ mSharedBottomEdge.put(childElement, parentElement);
+ }
+
+ if (mFlatten && isRemovableLayout(child)) {
+ // When flattening, we want to disregard all layouts and instead
+ // add their children!
+ for (CanvasViewInfo childView : child.getChildren()) {
+ analyze(childView, false);
+
+ Element childViewElement = getElement(childView);
+ Rectangle childViewBounds = childView.getAbsRect();
+
+ // See if this view shares the edge with the removed
+ // parent layout, and if so, record that such that we can
+ // later handle attachments to the removed parent edges
+ if (parentBounds.x == childViewBounds.x) {
+ mSharedLeftEdge.put(childViewElement, parentElement);
+ }
+ if (parentBounds.y == childViewBounds.y) {
+ mSharedTopEdge.put(childViewElement, parentElement);
+ }
+ if (parentBounds.x + parentBounds.width == childViewBounds.x
+ + childViewBounds.width) {
+ mSharedRightEdge.put(childViewElement, parentElement);
+ }
+ if (parentBounds.y + parentBounds.height == childViewBounds.y
+ + childViewBounds.height) {
+ mSharedBottomEdge.put(childViewElement, parentElement);
+ }
+ }
+ mDelete.add(childElement);
+ } else {
+ analyze(child, false);
+ }
+ }
+
+ return added;
+ }
+
+ public View getSharedLeftEdge(Element element) {
+ return getSharedEdge(element, mSharedLeftEdge);
+ }
+
+ public View getSharedRightEdge(Element element) {
+ return getSharedEdge(element, mSharedRightEdge);
+ }
+
+ public View getSharedTopEdge(Element element) {
+ return getSharedEdge(element, mSharedTopEdge);
+ }
+
+ public View getSharedBottomEdge(Element element) {
+ return getSharedEdge(element, mSharedBottomEdge);
+ }
+
+ private View getSharedEdge(Element element, Map<Element, Element> sharedEdgeMap) {
+ Element original = element;
+
+ while (element != null) {
+ View view = getView(element);
+ if (view != null) {
+ assert isAncestor(element, original);
+ return view;
+ }
+ element = sharedEdgeMap.get(element);
+ }
+
+ return null;
+ }
+
+ private View add(CanvasViewInfo info) {
+ Rectangle bounds = info.getAbsRect();
+ Element element = getElement(info);
+ View view = new View(info, element);
+ mElementToViewMap.put(element, view);
+ record(mLeft, Integer.valueOf(bounds.x), view);
+ record(mTop, Integer.valueOf(bounds.y), view);
+ record(mRight, Integer.valueOf(view.getRightEdge()), view);
+ record(mBottom, Integer.valueOf(view.getBottomEdge()), view);
+ return view;
+ }
+
+ /**
+ * Returns true if the given {@link CanvasViewInfo} represents an element we
+ * should remove in a flattening conversion. We don't want to remove non-layout
+ * views, or layout views that for example contain drawables on their own.
+ */
+ private boolean isRemovableLayout(CanvasViewInfo child) {
+ // The element being converted is NOT removable!
+ Element element = getElement(child);
+ if (element == mLayout) {
+ return false;
+ }
+
+ ElementDescriptor descriptor = child.getUiViewNode().getDescriptor();
+ String name = descriptor.getXmlLocalName();
+ if (name.equals(LINEAR_LAYOUT) || name.equals(RELATIVE_LAYOUT)) {
+ // Don't delete layouts that provide a background image or gradient
+ if (element.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND)) {
+ AdtPlugin.log(IStatus.WARNING,
+ "Did not flatten layout %1$s because it defines a '%2$s' attribute",
+ VisualRefactoring.getId(element), ATTR_BACKGROUND);
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java
new file mode 100644
index 0000000..87fdc14
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java
@@ -0,0 +1,1281 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
+import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS_COLON;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.text.edits.DeleteEdit;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Parent class for the various visual refactoring operations; contains shared
+ * implementations needed by most of them
+ */
+@SuppressWarnings("restriction") // XML model
+public abstract class VisualRefactoring extends Refactoring {
+ private static final String KEY_FILE = "file"; //$NON-NLS-1$
+ private static final String KEY_PROJECT = "proj"; //$NON-NLS-1$
+ private static final String KEY_SEL_START = "sel-start"; //$NON-NLS-1$
+ private static final String KEY_SEL_END = "sel-end"; //$NON-NLS-1$
+
+ protected final IFile mFile;
+ protected final LayoutEditor mEditor;
+ protected final IProject mProject;
+ protected int mSelectionStart = -1;
+ protected int mSelectionEnd = -1;
+ protected final List<Element> mElements;
+ protected final ITreeSelection mTreeSelection;
+ protected final ITextSelection mSelection;
+ /** Same as {@link #mSelectionStart} but not adjusted to element edges */
+ protected int mOriginalSelectionStart = -1;
+ /** Same as {@link #mSelectionEnd} but not adjusted to element edges */
+ protected int mOriginalSelectionEnd = -1;
+
+ protected final Map<Element, String> mGeneratedIdMap = new HashMap<Element, String>();
+ protected final Set<String> mGeneratedIds = new HashSet<String>();
+
+ protected List<Change> mChanges;
+ private String mAndroidNamespacePrefix;
+
+ /**
+ * This constructor is solely used by {@link VisualRefactoringDescriptor},
+ * to replay a previous refactoring.
+ * @param arguments argument map created by #createArgumentMap.
+ */
+ VisualRefactoring(Map<String, String> arguments) {
+ IPath path = Path.fromPortableString(arguments.get(KEY_PROJECT));
+ mProject = (IProject) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
+ path = Path.fromPortableString(arguments.get(KEY_FILE));
+ mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
+ mSelectionStart = Integer.parseInt(arguments.get(KEY_SEL_START));
+ mSelectionEnd = Integer.parseInt(arguments.get(KEY_SEL_END));
+ mOriginalSelectionStart = mSelectionStart;
+ mOriginalSelectionEnd = mSelectionEnd;
+ mEditor = null;
+ mElements = null;
+ mSelection = null;
+ mTreeSelection = null;
+ }
+
+ @VisibleForTesting
+ VisualRefactoring(List<Element> elements, LayoutEditor editor) {
+ mElements = elements;
+ mEditor = editor;
+
+ mFile = editor != null ? editor.getInputFile() : null;
+ mProject = editor != null ? editor.getProject() : null;
+ mSelectionStart = 0;
+ mSelectionEnd = 0;
+ mOriginalSelectionStart = 0;
+ mOriginalSelectionEnd = 0;
+ mSelection = null;
+ mTreeSelection = null;
+
+ int end = Integer.MIN_VALUE;
+ int start = Integer.MAX_VALUE;
+ for (Element element : elements) {
+ if (element instanceof IndexedRegion) {
+ IndexedRegion region = (IndexedRegion) element;
+ start = Math.min(start, region.getStartOffset());
+ end = Math.max(end, region.getEndOffset());
+ }
+ }
+ if (start >= 0) {
+ mSelectionStart = start;
+ mSelectionEnd = end;
+ mOriginalSelectionStart = start;
+ mOriginalSelectionEnd = end;
+ }
+ }
+
+ public VisualRefactoring(IFile file, LayoutEditor editor, ITextSelection selection,
+ ITreeSelection treeSelection) {
+ mFile = file;
+ mEditor = editor;
+ mProject = file.getProject();
+ mSelection = selection;
+ mTreeSelection = treeSelection;
+
+ // Initialize mSelectionStart and mSelectionEnd based on the selection context, which
+ // is either a treeSelection (when invoked from the layout editor or the outline), or
+ // a selection (when invoked from an XML editor)
+ if (treeSelection != null) {
+ int end = Integer.MIN_VALUE;
+ int start = Integer.MAX_VALUE;
+ for (TreePath path : treeSelection.getPaths()) {
+ Object lastSegment = path.getLastSegment();
+ if (lastSegment instanceof CanvasViewInfo) {
+ CanvasViewInfo viewInfo = (CanvasViewInfo) lastSegment;
+ UiViewElementNode uiNode = viewInfo.getUiViewNode();
+ if (uiNode == null) {
+ continue;
+ }
+ Node xmlNode = uiNode.getXmlNode();
+ if (xmlNode instanceof IndexedRegion) {
+ IndexedRegion region = (IndexedRegion) xmlNode;
+
+ start = Math.min(start, region.getStartOffset());
+ end = Math.max(end, region.getEndOffset());
+ }
+ }
+ }
+ if (start >= 0) {
+ mSelectionStart = start;
+ mSelectionEnd = end;
+ mOriginalSelectionStart = mSelectionStart;
+ mOriginalSelectionEnd = mSelectionEnd;
+ }
+ if (selection != null) {
+ mOriginalSelectionStart = selection.getOffset();
+ mOriginalSelectionEnd = mOriginalSelectionStart + selection.getLength();
+ }
+ } else if (selection != null) {
+ // TODO: update selection to boundaries!
+ mSelectionStart = selection.getOffset();
+ mSelectionEnd = mSelectionStart + selection.getLength();
+ mOriginalSelectionStart = mSelectionStart;
+ mOriginalSelectionEnd = mSelectionEnd;
+ }
+
+ mElements = initElements();
+ }
+
+ protected abstract List<Change> computeChanges(IProgressMonitor monitor);
+
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor monitor) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = new RefactoringStatus();
+ mChanges = new ArrayList<Change>();
+ try {
+ monitor.beginTask("Checking post-conditions...", 5);
+
+ // Reset state for each computeChanges call, in case the user goes back
+ // and forth in the refactoring wizard
+ mGeneratedIdMap.clear();
+ mGeneratedIds.clear();
+ List<Change> changes = computeChanges(monitor);
+ mChanges.addAll(changes);
+
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+
+ return status;
+ }
+
+ @Override
+ public Change createChange(IProgressMonitor monitor) throws CoreException,
+ OperationCanceledException {
+ try {
+ monitor.beginTask("Applying changes...", 1);
+
+ CompositeChange change = new CompositeChange(
+ getName(),
+ mChanges.toArray(new Change[mChanges.size()])) {
+ @Override
+ public ChangeDescriptor getDescriptor() {
+ VisualRefactoringDescriptor desc = createDescriptor();
+ return new RefactoringChangeDescriptor(desc);
+ }
+ };
+
+ monitor.worked(1);
+ return change;
+
+ } finally {
+ monitor.done();
+ }
+ }
+
+ protected abstract VisualRefactoringDescriptor createDescriptor();
+
+ protected Map<String, String> createArgumentMap() {
+ HashMap<String, String> args = new HashMap<String, String>();
+ args.put(KEY_PROJECT, mProject.getFullPath().toPortableString());
+ args.put(KEY_FILE, mFile.getFullPath().toPortableString());
+ args.put(KEY_SEL_START, Integer.toString(mSelectionStart));
+ args.put(KEY_SEL_END, Integer.toString(mSelectionEnd));
+
+ return args;
+ }
+
+ // ---- Shared functionality ----
+
+
+ protected void openFile(IFile file) {
+ GraphicalEditorPart graphicalEditor = mEditor.getGraphicalEditor();
+ IFile leavingFile = graphicalEditor.getEditedFile();
+
+ try {
+ // Duplicate the current state into the newly created file
+ QualifiedName qname = ConfigurationComposite.NAME_CONFIG_STATE;
+ String state = AdtPlugin.getFileProperty(leavingFile, qname);
+
+ // TODO: Look for a ".NoTitleBar.Fullscreen" theme version of the current
+ // theme to show.
+
+ file.setSessionProperty(GraphicalEditorPart.NAME_INITIAL_STATE, state);
+ } catch (CoreException e) {
+ // pass
+ }
+
+ /* TBD: "Show Included In" if supported.
+ * Not sure if this is a good idea.
+ if (graphicalEditor.renderingSupports(Capability.EMBEDDED_LAYOUT)) {
+ try {
+ Reference include = Reference.create(graphicalEditor.getEditedFile());
+ file.setSessionProperty(GraphicalEditorPart.NAME_INCLUDE, include);
+ } catch (CoreException e) {
+ // pass - worst that can happen is that we don't start with inclusion
+ }
+ }
+ */
+
+ try {
+ IEditorPart part = IDE.openEditor(mEditor.getEditorSite().getPage(), file);
+ if (part instanceof AndroidXmlEditor && AdtPrefs.getPrefs().getFormatXml()) {
+ AndroidXmlEditor newEditor = (AndroidXmlEditor) part;
+ newEditor.reformatDocument();
+ }
+ } catch (PartInitException e) {
+ AdtPlugin.log(e, "Can't open new included layout");
+ }
+ }
+
+
+ /** Produce a list of edits to replace references to the given id with the given new id */
+ protected static List<TextEdit> replaceIds(String androidNamePrefix,
+ IStructuredDocument doc, int skipStart, int skipEnd,
+ String rootId, String referenceId) {
+ if (rootId == null) {
+ return Collections.emptyList();
+ }
+
+ // We need to search for either @+id/ or @id/
+ String match1 = rootId;
+ String match2;
+ if (match1.startsWith(ID_PREFIX)) {
+ match2 = '"' + NEW_ID_PREFIX + match1.substring(ID_PREFIX.length()) + '"';
+ match1 = '"' + match1 + '"';
+ } else if (match1.startsWith(NEW_ID_PREFIX)) {
+ match2 = '"' + ID_PREFIX + match1.substring(NEW_ID_PREFIX.length()) + '"';
+ match1 = '"' + match1 + '"';
+ } else {
+ return Collections.emptyList();
+ }
+
+ String namePrefix = androidNamePrefix + ':' + ATTR_LAYOUT_PREFIX;
+ List<TextEdit> edits = new ArrayList<TextEdit>();
+
+ IStructuredDocumentRegion region = doc.getFirstStructuredDocumentRegion();
+ for (; region != null; region = region.getNext()) {
+ ITextRegionList list = region.getRegions();
+ int regionStart = region.getStart();
+
+ // Look at all attribute values and look for an id reference match
+ String attributeName = ""; //$NON-NLS-1$
+ for (int j = 0; j < region.getNumberOfRegions(); j++) {
+ ITextRegion subRegion = list.get(j);
+ String type = subRegion.getType();
+ if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(type)) {
+ attributeName = region.getText(subRegion);
+ } else if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(type)) {
+ // Only replace references in layout attributes
+ if (!attributeName.startsWith(namePrefix)) {
+ continue;
+ }
+ // Skip occurrences in the given skip range
+ int subRegionStart = regionStart + subRegion.getStart();
+ if (subRegionStart >= skipStart && subRegionStart <= skipEnd) {
+ continue;
+ }
+
+ String attributeValue = region.getText(subRegion);
+ if (attributeValue.equals(match1) || attributeValue.equals(match2)) {
+ int start = subRegionStart + 1; // skip quote
+ int end = start + rootId.length();
+
+ edits.add(new ReplaceEdit(start, end - start, referenceId));
+ }
+ }
+ }
+ }
+
+ return edits;
+ }
+
+ /** Get the id of the root selected element, if any */
+ protected String getRootId() {
+ Element primary = getPrimaryElement();
+ if (primary != null) {
+ String oldId = primary.getAttributeNS(ANDROID_URI, ATTR_ID);
+ // id null check for https://bugs.eclipse.org/bugs/show_bug.cgi?id=272378
+ if (oldId != null && oldId.length() > 0) {
+ return oldId;
+ }
+ }
+
+ return null;
+ }
+
+ protected String getAndroidNamespacePrefix() {
+ if (mAndroidNamespacePrefix == null) {
+ List<Attr> attributeNodes = findNamespaceAttributes();
+ for (Node attributeNode : attributeNodes) {
+ String prefix = attributeNode.getPrefix();
+ if (XMLNS.equals(prefix)) {
+ String name = attributeNode.getNodeName();
+ String value = attributeNode.getNodeValue();
+ if (value.equals(ANDROID_URI)) {
+ mAndroidNamespacePrefix = name;
+ if (mAndroidNamespacePrefix.startsWith(XMLNS_COLON)) {
+ mAndroidNamespacePrefix =
+ mAndroidNamespacePrefix.substring(XMLNS_COLON.length());
+ }
+ }
+ }
+ }
+
+ if (mAndroidNamespacePrefix == null) {
+ mAndroidNamespacePrefix = ANDROID_NS_NAME;
+ }
+ }
+
+ return mAndroidNamespacePrefix;
+ }
+
+ protected static String getAndroidNamespacePrefix(Document document) {
+ String nsPrefix = null;
+ List<Attr> attributeNodes = findNamespaceAttributes(document);
+ for (Node attributeNode : attributeNodes) {
+ String prefix = attributeNode.getPrefix();
+ if (XMLNS.equals(prefix)) {
+ String name = attributeNode.getNodeName();
+ String value = attributeNode.getNodeValue();
+ if (value.equals(ANDROID_URI)) {
+ nsPrefix = name;
+ if (nsPrefix.startsWith(XMLNS_COLON)) {
+ nsPrefix =
+ nsPrefix.substring(XMLNS_COLON.length());
+ }
+ }
+ }
+ }
+
+ if (nsPrefix == null) {
+ nsPrefix = ANDROID_NS_NAME;
+ }
+
+ return nsPrefix;
+ }
+
+ protected List<Attr> findNamespaceAttributes() {
+ Document document = getDomDocument();
+ return findNamespaceAttributes(document);
+ }
+
+ protected static List<Attr> findNamespaceAttributes(Document document) {
+ if (document != null) {
+ Element root = document.getDocumentElement();
+ return findNamespaceAttributes(root);
+ }
+
+ return Collections.emptyList();
+ }
+
+ protected static List<Attr> findNamespaceAttributes(Node root) {
+ List<Attr> result = new ArrayList<Attr>();
+ NamedNodeMap attributes = root.getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Node attributeNode = attributes.item(i);
+
+ String prefix = attributeNode.getPrefix();
+ if (XMLNS.equals(prefix)) {
+ result.add((Attr) attributeNode);
+ }
+ }
+
+ return result;
+ }
+
+ protected List<Attr> findLayoutAttributes(Node root) {
+ List<Attr> result = new ArrayList<Attr>();
+ NamedNodeMap attributes = root.getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Node attributeNode = attributes.item(i);
+
+ String name = attributeNode.getLocalName();
+ if (name.startsWith(ATTR_LAYOUT_PREFIX)
+ && ANDROID_URI.equals(attributeNode.getNamespaceURI())) {
+ result.add((Attr) attributeNode);
+ }
+ }
+
+ return result;
+ }
+
+ protected String insertNamespace(String xmlText, String namespaceDeclarations) {
+ // Insert namespace declarations into the extracted XML fragment
+ int firstSpace = xmlText.indexOf(' ');
+ int elementEnd = xmlText.indexOf('>');
+ int insertAt;
+ if (firstSpace != -1 && firstSpace < elementEnd) {
+ insertAt = firstSpace;
+ } else {
+ insertAt = elementEnd;
+ }
+ xmlText = xmlText.substring(0, insertAt) + namespaceDeclarations
+ + xmlText.substring(insertAt);
+
+ return xmlText;
+ }
+
+ /** Remove sections of the document that correspond to top level layout attributes;
+ * these are placed on the include element instead */
+ protected String stripTopLayoutAttributes(Element primary, int start, String xml) {
+ if (primary != null) {
+ // List of attributes to remove
+ List<IndexedRegion> skip = new ArrayList<IndexedRegion>();
+ NamedNodeMap attributes = primary.getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Node attr = attributes.item(i);
+ String name = attr.getLocalName();
+ if (name.startsWith(ATTR_LAYOUT_PREFIX)
+ && ANDROID_URI.equals(attr.getNamespaceURI())) {
+ if (name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT)) {
+ // These are special and are left in
+ continue;
+ }
+
+ if (attr instanceof IndexedRegion) {
+ skip.add((IndexedRegion) attr);
+ }
+ }
+ }
+ if (skip.size() > 0) {
+ Collections.sort(skip, new Comparator<IndexedRegion>() {
+ // Sort in start order
+ public int compare(IndexedRegion r1, IndexedRegion r2) {
+ return r1.getStartOffset() - r2.getStartOffset();
+ }
+ });
+
+ // Successively cut out the various layout attributes
+ // TODO remove adjacent whitespace too (but not newlines, unless they
+ // are newly adjacent)
+ StringBuilder sb = new StringBuilder(xml.length());
+ int nextStart = 0;
+
+ // Copy out all the sections except the skip sections
+ for (IndexedRegion r : skip) {
+ int regionStart = r.getStartOffset();
+ // Adjust to string offsets since we've copied the string out of
+ // the document
+ regionStart -= start;
+
+ sb.append(xml.substring(nextStart, regionStart));
+
+ nextStart = regionStart + r.getLength();
+ }
+ if (nextStart < xml.length()) {
+ sb.append(xml.substring(nextStart));
+ }
+
+ return sb.toString();
+ }
+ }
+
+ return xml;
+ }
+
+ protected static String getIndent(String line, int max) {
+ int i = 0;
+ int n = Math.min(max, line.length());
+ for (; i < n; i++) {
+ char c = line.charAt(i);
+ if (!Character.isWhitespace(c)) {
+ return line.substring(0, i);
+ }
+ }
+
+ if (n < line.length()) {
+ return line.substring(0, n);
+ } else {
+ return line;
+ }
+ }
+
+ protected static String dedent(String xml) {
+ String[] lines = xml.split("\n"); //$NON-NLS-1$
+ if (lines.length < 2) {
+ // The first line never has any indentation since we copy it out from the
+ // element start index
+ return xml;
+ }
+
+ String indentPrefix = getIndent(lines[1], lines[1].length());
+ for (int i = 2, n = lines.length; i < n; i++) {
+ String line = lines[i];
+
+ // Ignore blank lines
+ if (line.trim().length() == 0) {
+ continue;
+ }
+
+ indentPrefix = getIndent(line, indentPrefix.length());
+
+ if (indentPrefix.length() == 0) {
+ return xml;
+ }
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (String line : lines) {
+ if (line.startsWith(indentPrefix)) {
+ sb.append(line.substring(indentPrefix.length()));
+ } else {
+ sb.append(line);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ protected String getText(int start, int end) {
+ try {
+ IStructuredDocument document = mEditor.getStructuredDocument();
+ return document.get(start, end - start);
+ } catch (BadLocationException e) {
+ // the region offset was invalid. ignore.
+ return null;
+ }
+ }
+
+ protected List<Element> getElements() {
+ return mElements;
+ }
+
+ protected List<Element> initElements() {
+ List<Element> nodes = new ArrayList<Element>();
+
+ assert mTreeSelection == null || mSelection == null :
+ "treeSel= " + mTreeSelection + ", sel=" + mSelection;
+
+ // Initialize mSelectionStart and mSelectionEnd based on the selection context, which
+ // is either a treeSelection (when invoked from the layout editor or the outline), or
+ // a selection (when invoked from an XML editor)
+ if (mTreeSelection != null) {
+ int end = Integer.MIN_VALUE;
+ int start = Integer.MAX_VALUE;
+ for (TreePath path : mTreeSelection.getPaths()) {
+ Object lastSegment = path.getLastSegment();
+ if (lastSegment instanceof CanvasViewInfo) {
+ CanvasViewInfo viewInfo = (CanvasViewInfo) lastSegment;
+ UiViewElementNode uiNode = viewInfo.getUiViewNode();
+ if (uiNode == null) {
+ continue;
+ }
+ Node xmlNode = uiNode.getXmlNode();
+ if (xmlNode instanceof Element) {
+ Element element = (Element) xmlNode;
+ nodes.add(element);
+ IndexedRegion region = getRegion(element);
+ start = Math.min(start, region.getStartOffset());
+ end = Math.max(end, region.getEndOffset());
+ }
+ }
+ }
+ if (start >= 0) {
+ mSelectionStart = start;
+ mSelectionEnd = end;
+ }
+ } else if (mSelection != null) {
+ mSelectionStart = mSelection.getOffset();
+ mSelectionEnd = mSelectionStart + mSelection.getLength();
+ mOriginalSelectionStart = mSelectionStart;
+ mOriginalSelectionEnd = mSelectionEnd;
+
+ // Figure out the range of selected nodes from the document offsets
+ IStructuredDocument doc = mEditor.getStructuredDocument();
+ Pair<Element, Element> range = DomUtilities.getElementRange(doc,
+ mSelectionStart, mSelectionEnd);
+ if (range != null) {
+ Element first = range.getFirst();
+ Element last = range.getSecond();
+
+ // Adjust offsets to get rid of surrounding text nodes (if you happened
+ // to select a text range and included whitespace on either end etc)
+ mSelectionStart = getRegion(first).getStartOffset();
+ mSelectionEnd = getRegion(last).getEndOffset();
+
+ if (first == last) {
+ nodes.add(first);
+ } else if (first.getParentNode() == last.getParentNode()) {
+ // Add the range
+ Node node = first;
+ while (node != null) {
+ if (node instanceof Element) {
+ nodes.add((Element) node);
+ }
+ if (node == last) {
+ break;
+ }
+ node = node.getNextSibling();
+ }
+ } else {
+ // Different parents: this means we have an uneven selection, selecting
+ // elements from different levels. We can't extract ranges like that.
+ }
+ }
+ } else {
+ assert false;
+ }
+
+ // Make sure that the list of elements is unique
+ //Set<Element> seen = new HashSet<Element>();
+ //for (Element element : nodes) {
+ // assert !seen.contains(element) : element;
+ // seen.add(element);
+ //}
+
+ return nodes;
+ }
+
+ protected Element getPrimaryElement() {
+ List<Element> elements = getElements();
+ if (elements != null && elements.size() == 1) {
+ return elements.get(0);
+ }
+
+ return null;
+ }
+
+ protected Document getDomDocument() {
+ if (mEditor.getUiRootNode() != null) {
+ return mEditor.getUiRootNode().getXmlDocument();
+ } else {
+ return getElements().get(0).getOwnerDocument();
+ }
+ }
+
+ protected List<CanvasViewInfo> getSelectedViewInfos() {
+ List<CanvasViewInfo> infos = new ArrayList<CanvasViewInfo>();
+ if (mTreeSelection != null) {
+ for (TreePath path : mTreeSelection.getPaths()) {
+ Object lastSegment = path.getLastSegment();
+ if (lastSegment instanceof CanvasViewInfo) {
+ infos.add((CanvasViewInfo) lastSegment);
+ }
+ }
+ }
+ return infos;
+ }
+
+ protected boolean validateNotEmpty(List<CanvasViewInfo> infos, RefactoringStatus status) {
+ if (infos.size() == 0) {
+ status.addFatalError("No selection to extract");
+ return false;
+ }
+
+ return true;
+ }
+
+ protected boolean validateNotRoot(List<CanvasViewInfo> infos, RefactoringStatus status) {
+ for (CanvasViewInfo info : infos) {
+ if (info.isRoot()) {
+ status.addFatalError("Cannot refactor the root");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected boolean validateContiguous(List<CanvasViewInfo> infos, RefactoringStatus status) {
+ if (infos.size() > 1) {
+ // All elements must be siblings (e.g. same parent)
+ List<UiViewElementNode> nodes = new ArrayList<UiViewElementNode>(infos
+ .size());
+ for (CanvasViewInfo info : infos) {
+ UiViewElementNode node = info.getUiViewNode();
+ if (node != null) {
+ nodes.add(node);
+ }
+ }
+ if (nodes.size() == 0) {
+ status.addFatalError("No selected views");
+ return false;
+ }
+
+ UiElementNode parent = nodes.get(0).getUiParent();
+ for (UiViewElementNode node : nodes) {
+ if (parent != node.getUiParent()) {
+ status.addFatalError("The selected elements must be adjacent");
+ return false;
+ }
+ }
+ // Ensure that the siblings are contiguous; no gaps.
+ // If we've selected all the children of the parent then we don't need
+ // to look.
+ List<UiElementNode> siblings = parent.getUiChildren();
+ if (siblings.size() != nodes.size()) {
+ Set<UiViewElementNode> nodeSet = new HashSet<UiViewElementNode>(nodes);
+ boolean inRange = false;
+ int remaining = nodes.size();
+ for (UiElementNode node : siblings) {
+ boolean in = nodeSet.contains(node);
+ if (in) {
+ remaining--;
+ if (remaining == 0) {
+ break;
+ }
+ inRange = true;
+ } else if (inRange) {
+ status.addFatalError("The selected elements must be adjacent");
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Updates the given element with a new name if the current id reflects the old
+ * element type. If the name was changed, it will return the new name.
+ */
+ protected String ensureIdMatchesType(Element element, String newType, MultiTextEdit rootEdit) {
+ String oldType = element.getTagName();
+ if (oldType.indexOf('.') == -1) {
+ oldType = ANDROID_WIDGET_PREFIX + oldType;
+ }
+ String oldTypeBase = oldType.substring(oldType.lastIndexOf('.') + 1);
+ String id = getId(element);
+ if (id == null || id.toLowerCase().contains(oldTypeBase.toLowerCase())) {
+ String newTypeBase = newType.substring(newType.lastIndexOf('.') + 1);
+ return ensureHasId(rootEdit, element, newTypeBase);
+ }
+
+ return null;
+ }
+
+ public static IndexedRegion getRegion(Node node) {
+ if (node instanceof IndexedRegion) {
+ return (IndexedRegion) node;
+ }
+
+ return null;
+ }
+
+ protected String ensureHasId(MultiTextEdit rootEdit, Element element, String prefix) {
+ String id = mGeneratedIdMap.get(element);
+ if (id != null) {
+ return NEW_ID_PREFIX + id;
+ }
+
+ if (!element.hasAttributeNS(ANDROID_URI, ATTR_ID)
+ || (prefix != null && !getId(element).startsWith(prefix))) {
+ id = DomUtilities.getFreeWidgetId(element, mGeneratedIds, prefix);
+ // Make sure we don't use this one again
+ mGeneratedIds.add(id);
+ mGeneratedIdMap.put(element, id);
+ id = NEW_ID_PREFIX + id;
+ setAttribute(rootEdit, element,
+ ANDROID_URI, getAndroidNamespacePrefix(), ATTR_ID, id);
+ return id;
+ }
+
+ return getId(element);
+ }
+
+ protected int getFirstAttributeOffset(Element element) {
+ IndexedRegion region = getRegion(element);
+ if (region != null) {
+ int startOffset = region.getStartOffset();
+ int endOffset = region.getEndOffset();
+ String text = getText(startOffset, endOffset);
+ String name = element.getLocalName();
+ int nameOffset = text.indexOf(name);
+ if (nameOffset != -1) {
+ return startOffset + nameOffset + name.length();
+ }
+ }
+
+ return -1;
+ }
+
+ public static String getId(Element element) {
+ return element.getAttributeNS(ANDROID_URI, ATTR_ID);
+ }
+
+ protected String ensureNewId(String id) {
+ if (id != null && id.length() > 0) {
+ if (id.startsWith(ID_PREFIX)) {
+ id = NEW_ID_PREFIX + id.substring(ID_PREFIX.length());
+ } else if (!id.startsWith(NEW_ID_PREFIX)) {
+ id = NEW_ID_PREFIX + id;
+ }
+ } else {
+ id = null;
+ }
+
+ return id;
+ }
+
+ protected String getViewClass(String fqcn) {
+ // Don't include android.widget. as a package prefix in layout files
+ if (fqcn.startsWith(ANDROID_WIDGET_PREFIX)) {
+ fqcn = fqcn.substring(ANDROID_WIDGET_PREFIX.length());
+ }
+
+ return fqcn;
+ }
+
+ protected void setAttribute(MultiTextEdit rootEdit, Element element,
+ String attributeUri,
+ String attributePrefix, String attributeName, String attributeValue) {
+ int offset = getFirstAttributeOffset(element);
+ if (offset != -1) {
+ if (element.hasAttributeNS(attributeUri, attributeName)) {
+ replaceAttributeDeclaration(rootEdit, offset, element, attributePrefix,
+ attributeUri, attributeName, attributeValue);
+ } else {
+ addAttributeDeclaration(rootEdit, offset, attributePrefix, attributeName,
+ attributeValue);
+ }
+ }
+ }
+
+ private void addAttributeDeclaration(MultiTextEdit rootEdit, int offset,
+ String attributePrefix, String attributeName, String attributeValue) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(' ');
+
+ if (attributePrefix != null) {
+ sb.append(attributePrefix).append(':');
+ }
+ sb.append(attributeName).append('=').append('"');
+ sb.append(attributeValue).append('"');
+
+ InsertEdit setAttribute = new InsertEdit(offset, sb.toString());
+ rootEdit.addChild(setAttribute);
+ }
+
+ /** Replaces the value declaration of the given attribute */
+ private void replaceAttributeDeclaration(MultiTextEdit rootEdit, int offset,
+ Element element, String attributePrefix, String attributeUri,
+ String attributeName, String attributeValue) {
+ // Find attribute value and replace it
+ IStructuredModel model = mEditor.getModelForRead();
+ try {
+ IStructuredDocument doc = model.getStructuredDocument();
+
+ IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(offset);
+ ITextRegionList list = region.getRegions();
+ int regionStart = region.getStart();
+
+ int valueStart = -1;
+ boolean useNextValue = false;
+ String targetName = attributePrefix != null
+ ? attributePrefix + ':' + attributeName : attributeName;
+
+ // Look at all attribute values and look for an id reference match
+ for (int j = 0; j < region.getNumberOfRegions(); j++) {
+ ITextRegion subRegion = list.get(j);
+ String type = subRegion.getType();
+ if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(type)) {
+ // What about prefix?
+ if (targetName.equals(region.getText(subRegion))) {
+ useNextValue = true;
+ }
+ } else if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(type)) {
+ if (useNextValue) {
+ valueStart = regionStart + subRegion.getStart();
+ break;
+ }
+ }
+ }
+
+ if (valueStart != -1) {
+ String oldValue = element.getAttributeNS(attributeUri, attributeName);
+ int start = valueStart + 1; // Skip opening "
+ ReplaceEdit setAttribute = new ReplaceEdit(start, oldValue.length(),
+ attributeValue);
+ try {
+ rootEdit.addChild(setAttribute);
+ } catch (MalformedTreeException mte) {
+ AdtPlugin.log(mte, "Could not replace attribute %1$s with %2$s",
+ attributeName, attributeValue);
+ throw mte;
+ }
+ }
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+
+ /** Strips out the given attribute, if defined */
+ protected void removeAttribute(MultiTextEdit rootEdit, Element element, String uri,
+ String attributeName) {
+ if (element.hasAttributeNS(uri, attributeName)) {
+ Attr attribute = element.getAttributeNodeNS(uri, attributeName);
+ removeAttribute(rootEdit, attribute);
+ }
+ }
+
+ /** Strips out the given attribute, if defined */
+ protected void removeAttribute(MultiTextEdit rootEdit, Attr attribute) {
+ IndexedRegion region = getRegion(attribute);
+ if (region != null) {
+ int startOffset = region.getStartOffset();
+ int endOffset = region.getEndOffset();
+ DeleteEdit deletion = new DeleteEdit(startOffset, endOffset - startOffset);
+ rootEdit.addChild(deletion);
+ }
+ }
+
+
+ /**
+ * Removes the given element's opening and closing tags (including all of its
+ * attributes) but leaves any children alone
+ *
+ * @param rootEdit the multi edit to add the removal operation to
+ * @param element the element to delete the open and closing tags for
+ * @param skip a list of elements that should not be modified (for example because they
+ * are targeted for deletion)
+ *
+ * TODO: Rename this to "unwrap" ? And allow for handling nested deletions.
+ */
+ protected void removeElementTags(MultiTextEdit rootEdit, Element element, List<Element> skip) {
+ IndexedRegion elementRegion = getRegion(element);
+ if (elementRegion == null) {
+ return;
+ }
+
+ // Look for the opening tag
+ IStructuredModel model = mEditor.getModelForRead();
+ try {
+ int startLineInclusive = -1;
+ int endLineInclusive = -1;
+ IStructuredDocument doc = model.getStructuredDocument();
+ if (doc != null) {
+ int start = elementRegion.getStartOffset();
+ IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(start);
+ ITextRegionList list = region.getRegions();
+ int regionStart = region.getStart();
+ int startOffset = regionStart;
+ for (int j = 0; j < region.getNumberOfRegions(); j++) {
+ ITextRegion subRegion = list.get(j);
+ String type = subRegion.getType();
+ if (DOMRegionContext.XML_TAG_OPEN.equals(type)) {
+ startOffset = regionStart + subRegion.getStart();
+ } else if (DOMRegionContext.XML_TAG_CLOSE.equals(type)) {
+ int endOffset = regionStart + subRegion.getStart() + subRegion.getLength();
+
+ DeleteEdit deletion = createDeletion(doc, startOffset, endOffset);
+ rootEdit.addChild(deletion);
+ startLineInclusive = doc.getLineOfOffset(endOffset) + 1;
+ break;
+ }
+ }
+
+
+
+ // Find the close tag
+ // Look at all attribute values and look for an id reference match
+ region = doc.getRegionAtCharacterOffset(elementRegion.getEndOffset()
+ - element.getTagName().length() - 1);
+ list = region.getRegions();
+ regionStart = region.getStartOffset();
+ startOffset = -1;
+ for (int j = 0; j < region.getNumberOfRegions(); j++) {
+ ITextRegion subRegion = list.get(j);
+ String type = subRegion.getType();
+ if (DOMRegionContext.XML_END_TAG_OPEN.equals(type)) {
+ startOffset = regionStart + subRegion.getStart();
+ } else if (DOMRegionContext.XML_TAG_CLOSE.equals(type)) {
+ int endOffset = regionStart + subRegion.getStart() + subRegion.getLength();
+ if (startOffset != -1) {
+ DeleteEdit deletion = createDeletion(doc, startOffset, endOffset);
+ rootEdit.addChild(deletion);
+ endLineInclusive = doc.getLineOfOffset(startOffset) - 1;
+ }
+ break;
+ }
+ }
+ }
+
+ // Dedent the contents
+ if (startLineInclusive != -1 && endLineInclusive != -1) {
+ String indent = AndroidXmlEditor.getIndentAtOffset(doc, getRegion(element)
+ .getStartOffset());
+ setIndentation(rootEdit, indent, doc, startLineInclusive, endLineInclusive,
+ element, skip);
+ }
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+
+ protected void removeIndentation(MultiTextEdit rootEdit, String removeIndent,
+ IStructuredDocument doc, int startLineInclusive, int endLineInclusive,
+ Element element, List<Element> skip) {
+ if (startLineInclusive > endLineInclusive) {
+ return;
+ }
+ int indentLength = removeIndent.length();
+ if (indentLength == 0) {
+ return;
+ }
+
+ try {
+ for (int line = startLineInclusive; line <= endLineInclusive; line++) {
+ IRegion info = doc.getLineInformation(line);
+ int lineStart = info.getOffset();
+ int lineLength = info.getLength();
+ int lineEnd = lineStart + lineLength;
+ if (overlaps(lineStart, lineEnd, element, skip)) {
+ continue;
+ }
+ String lineText = getText(lineStart,
+ lineStart + Math.min(lineLength, indentLength));
+ if (lineText.startsWith(removeIndent)) {
+ rootEdit.addChild(new DeleteEdit(lineStart, indentLength));
+ }
+ }
+ } catch (BadLocationException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+
+ protected void setIndentation(MultiTextEdit rootEdit, String indent,
+ IStructuredDocument doc, int startLineInclusive, int endLineInclusive,
+ Element element, List<Element> skip) {
+ if (startLineInclusive > endLineInclusive) {
+ return;
+ }
+ int indentLength = indent.length();
+ if (indentLength == 0) {
+ return;
+ }
+
+ try {
+ for (int line = startLineInclusive; line <= endLineInclusive; line++) {
+ IRegion info = doc.getLineInformation(line);
+ int lineStart = info.getOffset();
+ int lineLength = info.getLength();
+ int lineEnd = lineStart + lineLength;
+ if (overlaps(lineStart, lineEnd, element, skip)) {
+ continue;
+ }
+ String lineText = getText(lineStart, lineStart + lineLength);
+ int indentEnd = getFirstNonSpace(lineText);
+ rootEdit.addChild(new ReplaceEdit(lineStart, indentEnd, indent));
+ }
+ } catch (BadLocationException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+
+ private int getFirstNonSpace(String s) {
+ for (int i = 0; i < s.length(); i++) {
+ if (!Character.isWhitespace(s.charAt(i))) {
+ return i;
+ }
+ }
+
+ return s.length();
+ }
+
+ /** Returns true if the given line overlaps any of the given elements */
+ private static boolean overlaps(int startOffset, int endOffset,
+ Element element, List<Element> overlaps) {
+ for (Element e : overlaps) {
+ if (e == element) {
+ continue;
+ }
+
+ IndexedRegion region = getRegion(e);
+ if (region.getEndOffset() >= startOffset && region.getStartOffset() <= endOffset) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected DeleteEdit createDeletion(IStructuredDocument doc, int startOffset, int endOffset) {
+ // Expand to delete the whole line?
+ try {
+ IRegion info = doc.getLineInformationOfOffset(startOffset);
+ int lineBegin = info.getOffset();
+ // Is the text on the line leading up to the deletion region,
+ // and the text following it, all whitespace?
+ boolean deleteLine = true;
+ if (lineBegin < startOffset) {
+ String prefix = getText(lineBegin, startOffset);
+ if (prefix.trim().length() > 0) {
+ deleteLine = false;
+ }
+ }
+ info = doc.getLineInformationOfOffset(endOffset);
+ int lineEnd = info.getOffset() + info.getLength();
+ if (lineEnd > endOffset) {
+ String suffix = getText(endOffset, lineEnd);
+ if (suffix.trim().length() > 0) {
+ deleteLine = false;
+ }
+ }
+ if (deleteLine) {
+ startOffset = lineBegin;
+ endOffset = lineEnd + 1;
+ }
+ } catch (BadLocationException e) {
+ AdtPlugin.log(e, null);
+ }
+
+
+ return new DeleteEdit(startOffset, endOffset - startOffset);
+ }
+
+ protected ViewElementDescriptor getElementDescriptor(String fqcn) {
+ AndroidTargetData data = mEditor.getTargetData();
+ if (data != null) {
+ List<ViewElementDescriptor> views =
+ data.getLayoutDescriptors().getViewDescriptors();
+ for (ViewElementDescriptor descriptor : views) {
+ if (fqcn.equals(descriptor.getFullClassName())) {
+ return descriptor;
+ }
+ }
+ List<ViewElementDescriptor> layouts =
+ data.getLayoutDescriptors().getLayoutDescriptors();
+ for (ViewElementDescriptor descriptor : layouts) {
+ if (fqcn.equals(descriptor.getFullClassName())) {
+ return descriptor;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /** Create a wizard for this refactoring */
+ abstract VisualRefactoringWizard createWizard();
+
+ public abstract static class VisualRefactoringDescriptor extends RefactoringDescriptor {
+ private final Map<String, String> mArguments;
+
+ public VisualRefactoringDescriptor(
+ String id, String project, String description, String comment,
+ Map<String, String> arguments) {
+ super(id, project, description, comment, STRUCTURAL_CHANGE | MULTI_CHANGE);
+ mArguments = arguments;
+ }
+
+ public Map<String, String> getArguments() {
+ return mArguments;
+ }
+
+ protected abstract Refactoring createRefactoring(Map<String, String> args);
+
+ @Override
+ public Refactoring createRefactoring(RefactoringStatus status) throws CoreException {
+ try {
+ return createRefactoring(mArguments);
+ } catch (NullPointerException e) {
+ status.addFatalError("Failed to recreate refactoring from descriptor");
+ return null;
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringAction.java
new file mode 100644
index 0000000..4818132
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringAction.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.FileEditorInput;
+
+abstract class VisualRefactoringAction implements IWorkbenchWindowActionDelegate {
+ protected IWorkbenchWindow mWindow;
+ protected ITextSelection mTextSelection;
+ protected ITreeSelection mTreeSelection;
+ protected LayoutEditor mEditor;
+ protected IFile mFile;
+
+ /**
+ * Keep track of the current workbench window.
+ */
+ public void init(IWorkbenchWindow window) {
+ mWindow = window;
+ }
+
+ public void dispose() {
+ }
+
+ /**
+ * Examine the selection to determine if the action should be enabled or not.
+ * <p/>
+ * Keep a link to the relevant selection structure
+ */
+ public void selectionChanged(IAction action, ISelection selection) {
+ // Look for selections in XML and in the layout UI editor
+
+ // Note, two kinds of selections are returned here:
+ // ITextSelection on a Java source window
+ // IStructuredSelection in the outline or navigator
+ // This simply deals with the refactoring based on a non-empty selection.
+ // At that point, just enable the action and later decide if it's valid when it actually
+ // runs since we don't have access to the AST yet.
+
+ mTextSelection = null;
+ mTreeSelection = null;
+ mFile = null;
+
+ IEditorPart editor = null;
+
+ if (selection instanceof ITextSelection) {
+ mTextSelection = (ITextSelection) selection;
+ editor = getActiveEditor();
+ mFile = getSelectedFile(editor);
+ } else if (selection instanceof ITreeSelection) {
+ Object firstElement = ((ITreeSelection)selection).getFirstElement();
+ if (firstElement instanceof CanvasViewInfo) {
+ mTreeSelection = (ITreeSelection) selection;
+ editor = getActiveEditor();
+ mFile = getSelectedFile(editor);
+ }
+ }
+
+
+ if (editor instanceof LayoutEditor) {
+ mEditor = (LayoutEditor) editor;
+ }
+
+ action.setEnabled((mTextSelection != null || mTreeSelection != null)
+ && mFile != null && mEditor != null);
+ }
+
+ /**
+ * Create a new instance of our refactoring and a wizard to configure it.
+ */
+ public abstract void run(IAction action);
+
+ /**
+ * Returns the active editor (hopefully matching our selection) or null.
+ */
+ private IEditorPart getActiveEditor() {
+ IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (wwin != null) {
+ IWorkbenchPage page = wwin.getActivePage();
+ if (page != null) {
+ return page.getActiveEditor();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the active {@link IFile} (hopefully matching our selection) or null.
+ * The file is only returned if it's a file from a project with an Android nature.
+ * <p/>
+ * At that point we do not try to analyze if the selection nor the file is suitable
+ * for the refactoring. This check is performed when the refactoring is invoked since
+ * it can then produce meaningful error messages as needed.
+ */
+ private IFile getSelectedFile(IEditorPart editor) {
+ if (editor != null) {
+ IEditorInput input = editor.getEditorInput();
+
+ if (input instanceof FileEditorInput) {
+ FileEditorInput fi = (FileEditorInput) input;
+ IFile file = fi.getFile();
+ if (file.exists()) {
+ IProject proj = file.getProject();
+ try {
+ if (proj != null && proj.hasNature(AdtConstants.NATURE_DEFAULT)) {
+ return file;
+ }
+ } catch (CoreException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public static IAction create(String title, LayoutEditor editor,
+ Class<? extends VisualRefactoringAction> clz) {
+ return new ActionWrapper(title, editor, clz);
+ }
+
+ private static class ActionWrapper extends Action {
+ private Class<? extends VisualRefactoringAction> mClass;
+ private LayoutEditor mEditor;
+
+ ActionWrapper(String title, LayoutEditor editor,
+ Class<? extends VisualRefactoringAction> clz) {
+ super(title);
+ mEditor = editor;
+ mClass = clz;
+ }
+
+ @Override
+ public void run() {
+ VisualRefactoringAction action;
+ try {
+ action = mClass.newInstance();
+ } catch (Exception e) {
+ AdtPlugin.log(e, null);
+ return;
+ }
+ action.init(mEditor.getEditorSite().getWorkbenchWindow());
+ ISelection selection = mEditor.getEditorSite().getSelectionProvider().getSelection();
+ action.selectionChanged(ActionWrapper.this, selection);
+ if (isEnabled()) {
+ action.run(ActionWrapper.this);
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringWizard.java
new file mode 100644
index 0000000..2b6f25e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringWizard.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public abstract class VisualRefactoringWizard extends RefactoringWizard {
+ protected final LayoutEditor mEditor;
+
+ public VisualRefactoringWizard(Refactoring refactoring, LayoutEditor editor) {
+ super(refactoring, DIALOG_BASED_USER_INTERFACE | PREVIEW_EXPAND_FIRST_NODE);
+ mEditor = editor;
+ }
+
+ @Override
+ public boolean performFinish() {
+ mEditor.setIgnoreXmlUpdate(true);
+ try {
+ return super.performFinish();
+ } finally {
+ mEditor.setIgnoreXmlUpdate(false);
+ mEditor.refreshXmlModel();
+ }
+ }
+
+ protected abstract static class VisualRefactoringInputPage extends UserInputWizardPage {
+ public VisualRefactoringInputPage(String name) {
+ super(name);
+ }
+
+ /**
+ * Listener which can be attached on any widget in the wizard page to force
+ * modifications of the associated widget to validate the page again
+ */
+ protected ModifyListener mModifyValidateListener = new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ validatePage();
+ }
+ };
+
+ /**
+ * Listener which can be attached on any widget in the wizard page to force
+ * selection changes of the associated widget to validate the page again
+ */
+ protected SelectionAdapter mSelectionValidateListener = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ validatePage();
+ }
+ };
+
+ protected abstract boolean validatePage();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInAction.java
new file mode 100644
index 0000000..1fd1837
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInAction.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+
+/**
+ * Action executed when the "Wrap In" menu item is invoked.
+ */
+public class WrapInAction extends VisualRefactoringAction {
+ @Override
+ public void run(IAction action) {
+ if ((mTextSelection != null || mTreeSelection != null) && mFile != null) {
+ WrapInRefactoring ref = new WrapInRefactoring(mFile, mEditor,
+ mTextSelection, mTreeSelection);
+ RefactoringWizard wizard = new WrapInWizard(ref, mEditor);
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ op.run(mWindow.getShell(), wizard.getDefaultPageTitle());
+ } catch (InterruptedException e) {
+ // Interrupted. Pass.
+ }
+ }
+ }
+
+ public static IAction create(LayoutEditor editor) {
+ return create("Wrap in Container...", editor, WrapInAction.class);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInContribution.java
new file mode 100644
index 0000000..61d7987
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInContribution.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+
+import java.util.Map;
+
+public class WrapInContribution extends RefactoringContribution {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public RefactoringDescriptor createDescriptor(String id, String project, String description,
+ String comment, Map arguments, int flags) throws IllegalArgumentException {
+ return new WrapInRefactoring.Descriptor(project, description, comment, arguments);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map retrieveArgumentMap(RefactoringDescriptor descriptor) {
+ if (descriptor instanceof WrapInRefactoring.Descriptor) {
+ return ((WrapInRefactoring.Descriptor) descriptor).getArguments();
+ }
+ return super.retrieveArgumentMap(descriptor);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoring.java
new file mode 100644
index 0000000..7a3744b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoring.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_FILL_PARENT;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_MATCH_PARENT;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.text.edits.DeleteEdit;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Inserts a new layout surrounding the current selection, migrates namespace
+ * attributes (if wrapping the root node), and optionally migrates layout
+ * attributes and updates references elsewhere.
+ */
+@SuppressWarnings("restriction") // XML model
+public class WrapInRefactoring extends VisualRefactoring {
+ private static final String KEY_ID = "name"; //$NON-NLS-1$
+ private static final String KEY_TYPE = "type"; //$NON-NLS-1$
+
+ private String mId;
+ private String mTypeFqcn;
+ private String mInitializedAttributes;
+
+ /**
+ * This constructor is solely used by {@link Descriptor},
+ * to replay a previous refactoring.
+ * @param arguments argument map created by #createArgumentMap.
+ */
+ WrapInRefactoring(Map<String, String> arguments) {
+ super(arguments);
+ mId = arguments.get(KEY_ID);
+ mTypeFqcn = arguments.get(KEY_TYPE);
+ }
+
+ public WrapInRefactoring(IFile file, LayoutEditor editor, ITextSelection selection,
+ ITreeSelection treeSelection) {
+ super(file, editor, selection, treeSelection);
+ }
+
+ @VisibleForTesting
+ WrapInRefactoring(List<Element> selectedElements, LayoutEditor editor) {
+ super(selectedElements, editor);
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = new RefactoringStatus();
+
+ try {
+ pm.beginTask("Checking preconditions...", 6);
+
+ if (mSelectionStart == -1 || mSelectionEnd == -1) {
+ status.addFatalError("No selection to wrap");
+ return status;
+ }
+
+ // Make sure the selection is contiguous
+ if (mTreeSelection != null) {
+ // TODO - don't do this if we based the selection on text. In this case,
+ // make sure we're -balanced-.
+
+ List<CanvasViewInfo> infos = getSelectedViewInfos();
+ if (!validateNotEmpty(infos, status)) {
+ return status;
+ }
+
+ // Enforce that the selection is -contiguous-
+ if (!validateContiguous(infos, status)) {
+ return status;
+ }
+ }
+
+ // Ensures that we have a valid DOM model:
+ if (mElements.size() == 0) {
+ status.addFatalError("Nothing to wrap");
+ return status;
+ }
+
+ pm.worked(1);
+ return status;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ @Override
+ protected VisualRefactoringDescriptor createDescriptor() {
+ String comment = getName();
+ return new Descriptor(
+ mProject.getName(), //project
+ comment, //description
+ comment, //comment
+ createArgumentMap());
+ }
+
+ @Override
+ protected Map<String, String> createArgumentMap() {
+ Map<String, String> args = super.createArgumentMap();
+ args.put(KEY_TYPE, mTypeFqcn);
+ args.put(KEY_ID, mId);
+
+ return args;
+ }
+
+ @Override
+ public String getName() {
+ return "Wrap in Container";
+ }
+
+ void setId(String id) {
+ mId = id;
+ }
+
+ void setType(String typeFqcn) {
+ mTypeFqcn = typeFqcn;
+ }
+
+ void setInitializedAttributes(String initializedAttributes) {
+ mInitializedAttributes = initializedAttributes;
+ }
+
+ @Override
+ protected List<Change> computeChanges(IProgressMonitor monitor) {
+ // (1) Insert the new container in front of the beginning of the
+ // first wrapped view
+ // (2) If the container is the new root, transfer namespace declarations
+ // to it
+ // (3) Insert the closing tag of the new container at the end of the
+ // last wrapped view
+ // (4) Reindent the wrapped views
+ // (5) If the user requested it, update all layout references to the
+ // wrapped views with the new container?
+ // For that matter, does RelativeLayout even require it? Probably not,
+ // it can point inside the current layout...
+
+ // Add indent to all lines between mSelectionStart and mEnd
+ // TODO: Figure out the indentation amount?
+ // For now, use 4 spaces
+ String indentUnit = " "; //$NON-NLS-1$
+ boolean separateAttributes = true;
+ IStructuredDocument document = mEditor.getStructuredDocument();
+ String startIndent = AndroidXmlEditor.getIndentAtOffset(document, mSelectionStart);
+
+ String viewClass = getViewClass(mTypeFqcn);
+ String androidNsPrefix = getAndroidNamespacePrefix();
+
+
+ IFile file = mEditor.getInputFile();
+ List<Change> changes = new ArrayList<Change>();
+ TextFileChange change = new TextFileChange(file.getName(), file);
+ MultiTextEdit rootEdit = new MultiTextEdit();
+ change.setEdit(rootEdit);
+ change.setTextType(EXT_XML);
+
+ String id = ensureNewId(mId);
+
+ // Update any layout references to the old id with the new id
+ if (id != null) {
+ String rootId = getRootId();
+ IStructuredModel model = mEditor.getModelForRead();
+ try {
+ IStructuredDocument doc = model.getStructuredDocument();
+ if (doc != null) {
+ List<TextEdit> replaceIds = replaceIds(androidNsPrefix,
+ doc, mSelectionStart, mSelectionEnd, rootId, id);
+ for (TextEdit edit : replaceIds) {
+ rootEdit.addChild(edit);
+ }
+ }
+ } finally {
+ model.releaseFromRead();
+ }
+ }
+
+ // Insert namespace elements?
+ StringBuilder namespace = null;
+ List<DeleteEdit> deletions = new ArrayList<DeleteEdit>();
+ Element primary = getPrimaryElement();
+ if (primary != null && getDomDocument().getDocumentElement() == primary) {
+ namespace = new StringBuilder();
+
+ List<Attr> declarations = findNamespaceAttributes(primary);
+ for (Attr attribute : declarations) {
+ if (attribute instanceof IndexedRegion) {
+ // Delete the namespace declaration in the node which is no longer the root
+ IndexedRegion region = (IndexedRegion) attribute;
+ int startOffset = region.getStartOffset();
+ int endOffset = region.getEndOffset();
+ String text = getText(startOffset, endOffset);
+ DeleteEdit deletion = new DeleteEdit(startOffset, endOffset - startOffset);
+ deletions.add(deletion);
+ rootEdit.addChild(deletion);
+ text = text.trim();
+
+ // Insert the namespace declaration in the new root
+ if (separateAttributes) {
+ namespace.append('\n').append(startIndent).append(indentUnit);
+ } else {
+ namespace.append(' ');
+ }
+ namespace.append(text);
+ }
+ }
+ }
+
+ // Insert begin tag: <type ...>
+ StringBuilder sb = new StringBuilder();
+ sb.append('<');
+ sb.append(viewClass);
+
+ if (namespace != null) {
+ sb.append(namespace);
+ }
+
+ // Set the ID if any
+ if (id != null) {
+ if (separateAttributes) {
+ sb.append('\n').append(startIndent).append(indentUnit);
+ } else {
+ sb.append(' ');
+ }
+ sb.append(androidNsPrefix).append(':');
+ sb.append(ATTR_ID).append('=').append('"').append(id).append('"');
+ }
+
+ // If any of the elements are fill/match parent, use that instead
+ String width = VALUE_WRAP_CONTENT;
+ String height = VALUE_WRAP_CONTENT;
+
+ for (Element element : getElements()) {
+ String oldWidth = element.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH);
+ String oldHeight = element.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT);
+
+ if (VALUE_MATCH_PARENT.equals(oldWidth) || VALUE_FILL_PARENT.equals(oldWidth)) {
+ width = oldWidth;
+ }
+ if (VALUE_MATCH_PARENT.equals(oldHeight) || VALUE_FILL_PARENT.equals(oldHeight)) {
+ height = oldHeight;
+ }
+ }
+
+ // Add in width/height.
+ if (separateAttributes) {
+ sb.append('\n').append(startIndent).append(indentUnit);
+ } else {
+ sb.append(' ');
+ }
+ sb.append(androidNsPrefix).append(':');
+ sb.append(ATTR_LAYOUT_WIDTH).append('=').append('"').append(width).append('"');
+
+ if (separateAttributes) {
+ sb.append('\n').append(startIndent).append(indentUnit);
+ } else {
+ sb.append(' ');
+ }
+ sb.append(androidNsPrefix).append(':');
+ sb.append(ATTR_LAYOUT_HEIGHT).append('=').append('"').append(height).append('"');
+
+ if (mInitializedAttributes != null && mInitializedAttributes.length() > 0) {
+ for (String s : mInitializedAttributes.split(",")) { //$NON-NLS-1$
+ sb.append(' ');
+ String[] nameValue = s.split("="); //$NON-NLS-1$
+ String name = nameValue[0];
+ String value = nameValue[1];
+ if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
+ name = name.substring(ANDROID_NS_NAME_PREFIX.length());
+ sb.append(androidNsPrefix).append(':');
+ }
+ sb.append(name).append('=').append('"').append(value).append('"');
+ }
+ }
+
+ // Transfer layout_ attributes (other than width and height)
+ if (primary != null) {
+ List<Attr> layoutAttributes = findLayoutAttributes(primary);
+ for (Attr attribute : layoutAttributes) {
+ String name = attribute.getLocalName();
+ if ((name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT))
+ && ANDROID_URI.equals(attribute.getNamespaceURI())) {
+ // Already handled specially
+ continue;
+ }
+
+ if (attribute instanceof IndexedRegion) {
+ IndexedRegion region = (IndexedRegion) attribute;
+ int startOffset = region.getStartOffset();
+ int endOffset = region.getEndOffset();
+ String text = getText(startOffset, endOffset);
+ DeleteEdit deletion = new DeleteEdit(startOffset, endOffset - startOffset);
+ rootEdit.addChild(deletion);
+ deletions.add(deletion);
+
+ if (separateAttributes) {
+ sb.append('\n').append(startIndent).append(indentUnit);
+ } else {
+ sb.append(' ');
+ }
+ sb.append(text.trim());
+ }
+ }
+ }
+
+ // Finish open tag:
+ sb.append('>');
+ sb.append('\n').append(startIndent).append(indentUnit);
+
+ InsertEdit beginEdit = new InsertEdit(mSelectionStart, sb.toString());
+ rootEdit.addChild(beginEdit);
+
+ String nested = getText(mSelectionStart, mSelectionEnd);
+ int index = 0;
+ while (index != -1) {
+ index = nested.indexOf('\n', index);
+ if (index != -1) {
+ index++;
+ InsertEdit newline = new InsertEdit(mSelectionStart + index, indentUnit);
+ // Some of the deleted namespaces may have had newlines - be careful
+ // not to overlap edits
+ boolean covered = false;
+ for (DeleteEdit deletion : deletions) {
+ if (deletion.covers(newline)) {
+ covered = true;
+ break;
+ }
+ }
+ if (!covered) {
+ rootEdit.addChild(newline);
+ }
+ }
+ }
+
+ // Insert end tag: </type>
+ sb.setLength(0);
+ sb.append('\n').append(startIndent);
+ sb.append('<').append('/').append(viewClass).append('>');
+ InsertEdit endEdit = new InsertEdit(mSelectionEnd, sb.toString());
+ rootEdit.addChild(endEdit);
+
+ changes.add(change);
+ return changes;
+ }
+
+ String getOldType() {
+ Element primary = getPrimaryElement();
+ if (primary != null) {
+ String oldType = primary.getTagName();
+ if (oldType.indexOf('.') == -1) {
+ oldType = ANDROID_WIDGET_PREFIX + oldType;
+ }
+ return oldType;
+ }
+
+ return null;
+ }
+
+ @Override
+ VisualRefactoringWizard createWizard() {
+ return new WrapInWizard(this, mEditor);
+ }
+
+ public static class Descriptor extends VisualRefactoringDescriptor {
+ public Descriptor(String project, String description, String comment,
+ Map<String, String> arguments) {
+ super("com.android.ide.eclipse.adt.refactoring.wrapin", //$NON-NLS-1$
+ project, description, comment, arguments);
+ }
+
+ @Override
+ protected Refactoring createRefactoring(Map<String, String> args) {
+ return new WrapInRefactoring(args);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInWizard.java
new file mode 100644
index 0000000..69df9a1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInWizard.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.FQCN_GESTURE_OVERLAY_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_LINEAR_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_RADIO_BUTTON;
+import static com.android.ide.common.layout.LayoutConstants.GESTURE_OVERLAY_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.RADIO_GROUP;
+import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_INCLUDE;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CustomViewFinder;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
+import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.resources.ResourceType;
+import com.android.sdklib.IAndroidTarget;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class WrapInWizard extends VisualRefactoringWizard {
+ private static final String SEPARATOR_LABEL =
+ "----------------------------------------"; //$NON-NLS-1$
+
+ public WrapInWizard(WrapInRefactoring ref, LayoutEditor editor) {
+ super(ref, editor);
+ setDefaultPageTitle("Wrap in Container");
+ }
+
+ @Override
+ protected void addUserInputPages() {
+ WrapInRefactoring ref = (WrapInRefactoring) getRefactoring();
+ String oldType = ref.getOldType();
+ addPage(new InputPage(mEditor.getProject(), oldType));
+ }
+
+ /** Wizard page which inputs parameters for the {@link WrapInRefactoring} operation */
+ private static class InputPage extends VisualRefactoringInputPage {
+ private final IProject mProject;
+ private final String mOldType;
+ private Text mIdText;
+ private Combo mTypeCombo;
+ private List<Pair<String, ViewElementDescriptor>> mClassNames;
+
+ public InputPage(IProject project, String oldType) {
+ super("WrapInInputPage"); //$NON-NLS-1$
+ mProject = project;
+ mOldType = oldType;
+ }
+
+ public void createControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(2, false));
+
+ Label typeLabel = new Label(composite, SWT.NONE);
+ typeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ typeLabel.setText("Type of Container:");
+
+ mTypeCombo = new Combo(composite, SWT.READ_ONLY);
+ mTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mTypeCombo.addSelectionListener(mSelectionValidateListener);
+
+ Label idLabel = new Label(composite, SWT.NONE);
+ idLabel.setText("New Layout Id:");
+ idLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+
+ mIdText = new Text(composite, SWT.BORDER);
+ mIdText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mIdText.addModifyListener(mModifyValidateListener);
+
+ Set<String> exclude = Collections.singleton(VIEW_INCLUDE);
+ mClassNames = addLayouts(mProject, mOldType, mTypeCombo, exclude, true);
+ mTypeCombo.select(0);
+
+ setControl(composite);
+ validatePage();
+
+ mTypeCombo.setFocus();
+ }
+
+ @Override
+ protected boolean validatePage() {
+ boolean ok = true;
+
+ String id = mIdText.getText().trim();
+
+ if (id.length() == 0) {
+ setErrorMessage("ID required");
+ ok = false;
+ } else {
+ // ...but if you do, it has to be valid!
+ ResourceNameValidator validator = ResourceNameValidator.create(false, mProject,
+ ResourceType.ID);
+ String message = validator.isValid(id);
+ if (message != null) {
+ setErrorMessage(message);
+ ok = false;
+ }
+ }
+
+ int selectionIndex = mTypeCombo.getSelectionIndex();
+ String type = selectionIndex != -1 ? mClassNames.get(selectionIndex).getFirst() : null;
+ if (type == null) {
+ setErrorMessage("Select a container type");
+ ok = false; // The user has chosen a separator
+ }
+
+ if (ok) {
+ setErrorMessage(null);
+
+ // Record state
+ WrapInRefactoring refactoring =
+ (WrapInRefactoring) getRefactoring();
+ refactoring.setId(id);
+ refactoring.setType(type);
+
+ ViewElementDescriptor descriptor = mClassNames.get(selectionIndex).getSecond();
+ if (descriptor instanceof PaletteMetadataDescriptor) {
+ PaletteMetadataDescriptor paletteDescriptor =
+ (PaletteMetadataDescriptor) descriptor;
+ String initializedAttributes = paletteDescriptor.getInitializedAttributes();
+ refactoring.setInitializedAttributes(initializedAttributes);
+ } else {
+ refactoring.setInitializedAttributes(null);
+ }
+ }
+
+ setPageComplete(ok);
+ return ok;
+ }
+ }
+
+ static List<Pair<String, ViewElementDescriptor>> addLayouts(IProject project,
+ String oldType, Combo combo,
+ Set<String> exclude, boolean addGestureOverlay) {
+ List<Pair<String, ViewElementDescriptor>> classNames =
+ new ArrayList<Pair<String, ViewElementDescriptor>>();
+
+ if (oldType != null && oldType.equals(FQCN_RADIO_BUTTON)) {
+ combo.add(RADIO_GROUP);
+ // NOT a fully qualified name since android widgets do not include the package
+ classNames.add(Pair.of(RADIO_GROUP, (ViewElementDescriptor) null));
+
+ combo.add(SEPARATOR_LABEL);
+ classNames.add(Pair.<String,ViewElementDescriptor>of(null, null));
+ }
+
+ Pair<List<String>,List<String>> result = CustomViewFinder.findViews(project, true);
+ List<String> customViews = result.getFirst();
+ List<String> thirdPartyViews = result.getSecond();
+ if (customViews.size() > 0) {
+ for (String view : customViews) {
+ combo.add(view);
+ classNames.add(Pair.of(view, (ViewElementDescriptor) null));
+ }
+ combo.add(SEPARATOR_LABEL);
+ classNames.add(Pair.<String,ViewElementDescriptor>of(null, null));
+ }
+
+ // Populate type combo
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IAndroidTarget target = currentSdk.getTarget(project);
+ if (target != null) {
+ AndroidTargetData targetData = currentSdk.getTargetData(target);
+ if (targetData != null) {
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+ List<Pair<String,List<ViewElementDescriptor>>> entries =
+ repository.getPaletteEntries(targetData, false, true);
+ // Find the layout category - it contains LinearLayout
+ List<ViewElementDescriptor> layoutDescriptors = null;
+
+ search: for (Pair<String,List<ViewElementDescriptor>> pair : entries) {
+ List<ViewElementDescriptor> list = pair.getSecond();
+ for (ViewElementDescriptor d : list) {
+ if (d.getFullClassName().equals(FQCN_LINEAR_LAYOUT)) {
+ // Found - use this list
+ layoutDescriptors = list;
+ break search;
+ }
+ }
+ }
+ if (layoutDescriptors != null) {
+ for (ViewElementDescriptor d : layoutDescriptors) {
+ String className = d.getFullClassName();
+ if (exclude == null || !exclude.contains(className)) {
+ combo.add(d.getUiName());
+ classNames.add(Pair.of(className, d));
+ }
+ }
+
+ // SWT does not support separators in combo boxes
+ combo.add(SEPARATOR_LABEL);
+ classNames.add(null);
+
+ if (thirdPartyViews.size() > 0) {
+ for (String view : thirdPartyViews) {
+ combo.add(view);
+ classNames.add(Pair.of(view, (ViewElementDescriptor) null));
+ }
+ combo.add(SEPARATOR_LABEL);
+ classNames.add(null);
+ }
+
+ if (addGestureOverlay) {
+ combo.add(GESTURE_OVERLAY_VIEW);
+ classNames.add(Pair.<String, ViewElementDescriptor> of(
+ FQCN_GESTURE_OVERLAY_VIEW, null));
+
+ combo.add(SEPARATOR_LABEL);
+ classNames.add(Pair.<String,ViewElementDescriptor>of(null, null));
+ }
+ }
+
+ // Now add ALL known layout descriptors in case the user has
+ // a special case
+ layoutDescriptors =
+ targetData.getLayoutDescriptors().getLayoutDescriptors();
+
+ for (ViewElementDescriptor d : layoutDescriptors) {
+ String className = d.getFullClassName();
+ if (exclude == null || !exclude.equals(className)) {
+ combo.add(d.getUiName());
+ classNames.add(Pair.of(className, d));
+ }
+ }
+ }
+ }
+ } else {
+ combo.add("SDK not initialized");
+ classNames.add(Pair.<String,ViewElementDescriptor>of(null, null));
+ }
+
+ return classNames;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java
index f4b10b7..60b5662 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java
@@ -112,7 +112,7 @@ public class UiViewElementNode extends UiElementNode {
layout_attrs.length);
if (need_xmlns) {
AttributeDescriptor desc = new XmlnsAttributeDescriptor(
- LayoutConstants.ANDROID_NS_PREFIX,
+ LayoutConstants.ANDROID_NS_NAME,
SdkConstants.NS_RESOURCES);
mCachedAttributeDescriptors[direct_attrs.length + layout_attrs.length] = desc;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestContentAssist.java
index 349048f..1c92309 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestContentAssist.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestContentAssist.java
@@ -16,16 +16,18 @@
package com.android.ide.eclipse.adt.internal.editors.manifest;
+import com.android.annotations.VisibleForTesting;
import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
/**
* Content Assist Processor for AndroidManifest.xml
*/
-final class ManifestContentAssist extends AndroidContentAssist {
+@VisibleForTesting
+public final class ManifestContentAssist extends AndroidContentAssist {
/**
- * Constructor for ManifestContentAssist
+ * Constructor for ManifestContentAssist
*/
public ManifestContentAssist() {
super(AndroidTargetData.DESCRIPTOR_MANIFEST);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestEditor.java
index fb742a7..38a5e6b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestEditor.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.editors.manifest;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
@@ -54,9 +54,10 @@ import javax.xml.xpath.XPathExpressionException;
/**
* Multi-page form editor for AndroidManifest.xml.
*/
+@SuppressWarnings("restriction")
public final class ManifestEditor extends AndroidXmlEditor {
- public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".manifest.ManifestEditor"; //$NON-NLS-1$
+ public static final String ID = AdtConstants.EDITORS_NAMESPACE + ".manifest.ManifestEditor"; //$NON-NLS-1$
private final static String EMPTY = ""; //$NON-NLS-1$
@@ -197,7 +198,6 @@ public final class ManifestEditor extends AndroidXmlEditor {
return null;
}
- @SuppressWarnings("restriction")
private void onDescriptorsChanged() {
IStructuredModel model = getModelForRead();
if (model != null) {
@@ -256,7 +256,7 @@ public final class ManifestEditor extends AndroidXmlEditor {
try {
// get the markers for the file
IMarker[] markers = inputFile.findMarkers(
- AndroidConstants.MARKER_ANDROID, true, IResource.DEPTH_ZERO);
+ AdtConstants.MARKER_ANDROID, true, IResource.DEPTH_ZERO);
AndroidManifestDescriptors desc = getManifestDescriptors();
if (desc != null) {
@@ -304,12 +304,12 @@ public final class ManifestEditor extends AndroidXmlEditor {
*/
private void processMarker(IMarker marker, List<UiElementNode> nodeList, int kind) {
// get the data from the marker
- String nodeType = marker.getAttribute(AndroidConstants.MARKER_ATTR_TYPE, EMPTY);
+ String nodeType = marker.getAttribute(AdtConstants.MARKER_ATTR_TYPE, EMPTY);
if (nodeType == EMPTY) {
return;
}
- String className = marker.getAttribute(AndroidConstants.MARKER_ATTR_CLASS, EMPTY);
+ String className = marker.getAttribute(AdtConstants.MARKER_ATTR_CLASS, EMPTY);
if (className == EMPTY) {
return;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfo.java
new file mode 100644
index 0000000..0feab55
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfo.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2011 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.editors.manifest;
+
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_STYLE;
+import static com.android.sdklib.SdkConstants.NS_RESOURCES;
+import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_ICON;
+import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_LABEL;
+import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION;
+import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_NAME;
+import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_PACKAGE;
+import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_TARGET_SDK_VERSION;
+import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_THEME;
+import static com.android.sdklib.xml.AndroidManifest.NODE_ACTIVITY;
+import static com.android.sdklib.xml.AndroidManifest.NODE_USES_SDK;
+import static org.eclipse.jdt.core.search.IJavaSearchConstants.REFERENCES;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.io.IFolderWrapper;
+import com.android.io.IAbstractFile;
+import com.android.resources.ScreenSize;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.xml.AndroidManifest;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.SearchEngine;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchParticipant;
+import org.eclipse.jdt.core.search.SearchPattern;
+import org.eclipse.jdt.core.search.SearchRequestor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ui.editors.text.TextFileDocumentProvider;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * Retrieves and caches manifest information such as the themes to be used for
+ * a given activity.
+ *
+ * @see AndroidManifest
+ */
+public class ManifestInfo {
+ /**
+ * The maximum number of milliseconds to search for an activity in the codebase when
+ * attempting to associate layouts with activities in
+ * {@link #guessActivity(IFile, String)}
+ */
+ private static final int SEARCH_TIMEOUT_MS = 3000;
+
+ private final IProject mProject;
+ private String mPackage;
+ private String mManifestTheme;
+ private Map<String, String> mActivityThemes;
+ private IAbstractFile mManifestFile;
+ private long mLastModified;
+ private int mTargetSdk;
+ private String mApplicationIcon;
+ private String mApplicationLabel;
+
+ /**
+ * Qualified name for the per-project non-persistent property storing the
+ * {@link ManifestInfo} for this project
+ */
+ final static QualifiedName MANIFEST_FINDER = new QualifiedName(AdtPlugin.PLUGIN_ID,
+ "manifest"); //$NON-NLS-1$
+
+ /**
+ * Constructs an {@link ManifestInfo} for the given project. Don't use this method;
+ * use the {@link #get} factory method instead.
+ *
+ * @param project project to create an {@link ManifestInfo} for
+ */
+ private ManifestInfo(IProject project) {
+ mProject = project;
+ }
+
+ /**
+ * Returns the {@link ManifestInfo} for the given project
+ *
+ * @param project the project the finder is associated with
+ * @return a {@ManifestInfo} for the given project, never null
+ */
+ public static ManifestInfo get(IProject project) {
+ ManifestInfo finder = null;
+ try {
+ finder = (ManifestInfo) project.getSessionProperty(MANIFEST_FINDER);
+ } catch (CoreException e) {
+ // Not a problem; we will just create a new one
+ }
+
+ if (finder == null) {
+ finder = new ManifestInfo(project);
+ try {
+ project.setSessionProperty(MANIFEST_FINDER, finder);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, "Can't store ManifestInfo");
+ }
+ }
+
+ return finder;
+ }
+
+ /**
+ * Ensure that the package, theme and activity maps are initialized and up to date
+ * with respect to the manifest file
+ */
+ private void sync() {
+ if (mManifestFile == null) {
+ IFolderWrapper projectFolder = new IFolderWrapper(mProject);
+ mManifestFile = AndroidManifest.getManifest(projectFolder);
+ if (mManifestFile == null) {
+ return;
+ }
+ }
+
+ // Check to see if our data is up to date
+ long fileModified = mManifestFile.getModificationStamp();
+ if (fileModified == mLastModified) {
+ // Already have up to date data
+ return;
+ }
+ mLastModified = fileModified;
+
+ mActivityThemes = new HashMap<String, String>();
+ mManifestTheme = null;
+ mTargetSdk = 1; // Default when not specified
+ mPackage = ""; //$NON-NLS-1$
+ mApplicationIcon = null;
+ mApplicationLabel = null;
+
+ Document document = null;
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ InputSource is = new InputSource(mManifestFile.getContents());
+
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ document = builder.parse(is);
+
+ Element root = document.getDocumentElement();
+ mPackage = root.getAttribute(ATTRIBUTE_PACKAGE);
+ NodeList activities = document.getElementsByTagName(NODE_ACTIVITY);
+ for (int i = 0, n = activities.getLength(); i < n; i++) {
+ Element activity = (Element) activities.item(i);
+ String theme = activity.getAttributeNS(NS_RESOURCES, ATTRIBUTE_THEME);
+ if (theme != null && theme.length() > 0) {
+ String name = activity.getAttributeNS(NS_RESOURCES, ATTRIBUTE_NAME);
+ if (name.startsWith(".") //$NON-NLS-1$
+ && mPackage != null && mPackage.length() > 0) {
+ name = mPackage + name;
+ }
+ mActivityThemes.put(name, theme);
+ }
+ }
+
+ NodeList applications = root.getElementsByTagName(AndroidManifest.NODE_APPLICATION);
+ if (applications.getLength() > 0) {
+ assert applications.getLength() == 1;
+ Element application = (Element) applications.item(0);
+ if (application.hasAttributeNS(NS_RESOURCES, ATTRIBUTE_ICON)) {
+ mApplicationIcon = application.getAttributeNS(NS_RESOURCES, ATTRIBUTE_ICON);
+ }
+ if (application.hasAttributeNS(NS_RESOURCES, ATTRIBUTE_LABEL)) {
+ mApplicationLabel = application.getAttributeNS(NS_RESOURCES, ATTRIBUTE_LABEL);
+ }
+ }
+
+ // Look up target SDK
+ String defaultTheme = root.getAttributeNS(NS_RESOURCES, ATTRIBUTE_THEME);
+ if (defaultTheme == null || defaultTheme.length() == 0) {
+ // From manifest theme documentation:
+ // "If that attribute is also not set, the default system theme is used."
+
+ NodeList usesSdks = root.getElementsByTagName(NODE_USES_SDK);
+ if (usesSdks.getLength() > 0) {
+ Element usesSdk = (Element) usesSdks.item(0);
+ String targetSdk = null;
+ if (usesSdk.hasAttributeNS(NS_RESOURCES, ATTRIBUTE_TARGET_SDK_VERSION)) {
+ targetSdk = usesSdk.getAttributeNS(NS_RESOURCES,
+ ATTRIBUTE_TARGET_SDK_VERSION);
+ } else if (usesSdk.hasAttributeNS(NS_RESOURCES, ATTRIBUTE_MIN_SDK_VERSION)) {
+ targetSdk = usesSdk.getAttributeNS(NS_RESOURCES,
+ ATTRIBUTE_MIN_SDK_VERSION);
+ }
+ if (targetSdk != null) {
+ int apiLevel = -1;
+ try {
+ apiLevel = Integer.valueOf(targetSdk);
+ } catch (NumberFormatException e) {
+ // Handle codename
+ if (Sdk.getCurrent() != null) {
+ IAndroidTarget target = Sdk.getCurrent().getTargetFromHashString(
+ "android-" + targetSdk); //$NON-NLS-1$
+ if (target != null) {
+ // codename future API level is current api + 1
+ apiLevel = target.getVersion().getApiLevel() + 1;
+ }
+ }
+ }
+
+ mTargetSdk = apiLevel;
+ }
+ }
+ } else {
+ mManifestTheme = defaultTheme;
+ }
+ } catch (SAXException e) {
+ AdtPlugin.log(e, "Malformed manifest");
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Could not read Manifest data");
+ }
+ }
+
+ /**
+ * Returns the default package registered in the Android manifest
+ *
+ * @return the default package registered in the manifest
+ */
+ public String getPackage() {
+ sync();
+ return mPackage;
+ }
+
+ /**
+ * Returns a map from activity full class names to the corresponding theme style to be
+ * used
+ *
+ * @return a map from activity fqcn to theme style
+ */
+ public Map<String, String> getActivityThemes() {
+ sync();
+ return mActivityThemes;
+ }
+
+ /**
+ * Returns the default theme for this project, by looking at the manifest default
+ * theme registration, target SDK, rendering target, etc.
+ *
+ * @param renderingTarget the rendering target use to render the theme, or null
+ * @param screenSize the screen size to obtain a default theme for, or null if unknown
+ * @return the theme to use for this project, never null
+ */
+ public String getDefaultTheme(IAndroidTarget renderingTarget, ScreenSize screenSize) {
+ sync();
+
+ if (mManifestTheme != null) {
+ return mManifestTheme;
+ }
+
+ int renderingTargetSdk = mTargetSdk;
+ if (renderingTarget != null) {
+ renderingTargetSdk = renderingTarget.getVersion().getApiLevel();
+ }
+
+ int apiLevel = Math.min(mTargetSdk, renderingTargetSdk);
+ // For now this theme works only on XLARGE screens. When it works for all sizes,
+ // add that new apiLevel to this check.
+ if (apiLevel >= 11 && screenSize == ScreenSize.XLARGE) {
+ return PREFIX_ANDROID_STYLE + "Theme.Holo"; //$NON-NLS-1$
+ } else {
+ return PREFIX_ANDROID_STYLE + "Theme"; //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Returns the application icon, or null
+ *
+ * @return the application icon, or null
+ */
+ public String getApplicationIcon() {
+ sync();
+ return mApplicationIcon;
+ }
+
+ /**
+ * Returns the application label, or null
+ *
+ * @return the application label, or null
+ */
+ public String getApplicationLabel() {
+ sync();
+ return mApplicationLabel;
+ }
+
+ /**
+ * Returns the activity associated with the given layout file. Makes an educated guess
+ * by peeking at the usages of the R.layout.name field corresponding to the layout and
+ * if it finds a usage.
+ *
+ * @param project the project containing the layout
+ * @param layoutName the layout whose activity we want to look up
+ * @param pkg the package containing activities
+ * @return the activity name
+ */
+ public static String guessActivity(IProject project, String layoutName, String pkg) {
+ final AtomicReference<String> activity = new AtomicReference<String>();
+ SearchRequestor requestor = new SearchRequestor() {
+ @Override
+ public void acceptSearchMatch(SearchMatch match) throws CoreException {
+ Object element = match.getElement();
+ if (element instanceof IMethod) {
+ IMethod method = (IMethod) element;
+ IType declaringType = method.getDeclaringType();
+ String fqcn = declaringType.getFullyQualifiedName();
+ if (activity.get() == null
+ || declaringType.getSuperclassName().endsWith("Activity") //$NON-NLS-1$
+ || method.getElementName().equals("onCreate")) { //$NON-NLS-1$
+ activity.set(fqcn);
+ }
+ }
+ }
+ };
+ try {
+ IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
+ if (javaProject == null) {
+ return null;
+ }
+ // TODO - look around a bit more and see if we can figure out whether the
+ // call if from within a setContentView call!
+
+ // Search for which java classes call setContentView(R.layout.layoutname);
+ String typeFqcn = "R.layout"; //$NON-NLS-1$
+ if (pkg != null) {
+ typeFqcn = pkg + '.' + typeFqcn;
+ }
+
+ IType type = javaProject.findType(typeFqcn);
+ if (type != null) {
+ IField field = type.getField(layoutName);
+ if (field.exists()) {
+ SearchPattern pattern = SearchPattern.createPattern(field, REFERENCES);
+ search(requestor, javaProject, pattern);
+ }
+ }
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+
+ return activity.get();
+ }
+
+ /**
+ * Returns the activity associated with the given layout file.
+ * <p>
+ * This is an alternative to {@link #guessActivity(IFile, String)}. Whereas
+ * guessActivity simply looks for references to "R.layout.foo", this method searches
+ * for all usages of Activity#setContentView(int), and for each match it looks up the
+ * corresponding call text (such as "setContentView(R.layout.foo)"). From this it uses
+ * a regexp to pull out "foo" from this, and stores the association that layout "foo"
+ * is associated with the activity class that contained the setContentView call.
+ * <p>
+ * This has two potential advantages:
+ * <ol>
+ * <li>It can be faster. We do the reference search -once-, and we've built a map of
+ * all the layout-to-activity mappings which we can then immediately look up other
+ * layouts for, which is particularly useful at startup when we have to compute the
+ * layout activity associations to populate the theme choosers.
+ * <li>It can be more accurate. Just because an activity references an "R.layout.foo"
+ * field doesn't mean it's setting it as a content view.
+ * </ol>
+ * However, this second advantage is also its chief problem. There are some common
+ * code constructs which means that the associated layout is not explicitly referenced
+ * in a direct setContentView call; on a couple of sample projects I tested I found
+ * patterns like for example "setContentView(v)" where "v" had been computed earlier.
+ * Therefore, for now we're going to stick with the more general approach of just
+ * looking up each field when needed. We're keeping the code around, though statically
+ * compiled out with the "if (false)" construct below in case we revisit this.
+ *
+ * @param layoutFile the layout whose activity we want to look up
+ * @return the activity name
+ */
+ @SuppressWarnings("all")
+ public String guessActivityBySetContentView(String layoutName) {
+ if (false) {
+ // These should be fields
+ final Pattern LAYOUT_FIELD_PATTERN =
+ Pattern.compile("R\\.layout\\.([a-z0-9_]+)"); //$NON-NLS-1$
+ Map<String, String> mUsages = null;
+
+ sync();
+ if (mUsages == null) {
+ final Map<String, String> usages = new HashMap<String, String>();
+ mUsages = usages;
+ SearchRequestor requestor = new SearchRequestor() {
+ @Override
+ public void acceptSearchMatch(SearchMatch match) throws CoreException {
+ Object element = match.getElement();
+ if (element instanceof IMethod) {
+ IMethod method = (IMethod) element;
+ IType declaringType = method.getDeclaringType();
+ String fqcn = declaringType.getFullyQualifiedName();
+ IDocumentProvider provider = new TextFileDocumentProvider();
+ IResource resource = match.getResource();
+ try {
+ provider.connect(resource);
+ IDocument document = provider.getDocument(resource);
+ if (document != null) {
+ String matchText = document.get(match.getOffset(),
+ match.getLength());
+ Matcher matcher = LAYOUT_FIELD_PATTERN.matcher(matchText);
+ if (matcher.find()) {
+ usages.put(matcher.group(1), fqcn);
+ }
+ }
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Can't find range information for %1$s",
+ resource.getName());
+ } finally {
+ provider.disconnect(resource);
+ }
+ }
+ }
+ };
+ try {
+ IJavaProject javaProject = BaseProjectHelper.getJavaProject(mProject);
+ if (javaProject == null) {
+ return null;
+ }
+
+ // Search for which java classes call setContentView(R.layout.layoutname);
+ String typeFqcn = "R.layout"; //$NON-NLS-1$
+ if (mPackage != null) {
+ typeFqcn = mPackage + '.' + typeFqcn;
+ }
+
+ IType activityType = javaProject.findType(SdkConstants.CLASS_ACTIVITY);
+ if (activityType != null) {
+ IMethod method = activityType.getMethod(
+ "setContentView", new String[] {"I"}); //$NON-NLS-1$ //$NON-NLS-2$
+ if (method.exists()) {
+ SearchPattern pattern = SearchPattern.createPattern(method,
+ REFERENCES);
+ search(requestor, javaProject, pattern);
+ }
+ }
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+
+ return mUsages.get(layoutName);
+ }
+
+ return null;
+ }
+
+ /**
+ * Performs a search using the given pattern, scope and handler. The search will abort
+ * if it takes longer than {@link #SEARCH_TIMEOUT_MS} milliseconds.
+ */
+ private static void search(SearchRequestor requestor, IJavaProject javaProject,
+ SearchPattern pattern) throws CoreException {
+ // Find the package fragment specified in the manifest; the activities should
+ // live there.
+ IJavaSearchScope scope = createPackageScope(javaProject);
+
+ SearchParticipant[] participants = new SearchParticipant[] {
+ SearchEngine.getDefaultSearchParticipant()
+ };
+ SearchEngine engine = new SearchEngine();
+
+ final long searchStart = System.currentTimeMillis();
+ NullProgressMonitor monitor = new NullProgressMonitor() {
+ private boolean mCancelled;
+ @Override
+ public void internalWorked(double work) {
+ long searchEnd = System.currentTimeMillis();
+ if (searchEnd - searchStart > SEARCH_TIMEOUT_MS) {
+ mCancelled = true;
+ }
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return mCancelled;
+ }
+ };
+ engine.search(pattern, participants, scope, requestor, monitor);
+ }
+
+ /** Creates a package search scope for the first package root in the given java project */
+ private static IJavaSearchScope createPackageScope(IJavaProject javaProject) {
+ IPackageFragmentRoot packageRoot = getSourcePackageRoot(javaProject);
+
+ IJavaSearchScope scope;
+ if (packageRoot != null) {
+ IJavaElement[] scopeElements = new IJavaElement[] { packageRoot };
+ scope = SearchEngine.createJavaSearchScope(scopeElements);
+ } else {
+ scope = SearchEngine.createWorkspaceScope();;
+ }
+ return scope;
+ }
+
+ /** Returns the first package root for the given java project */
+ private static IPackageFragmentRoot getSourcePackageRoot(IJavaProject javaProject) {
+ IPackageFragmentRoot packageRoot = null;
+ List<IPath> sources = BaseProjectHelper.getSourceClasspaths(javaProject);
+
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ for (IPath path : sources) {
+ IResource firstSource = workspace.getRoot().findMember(path);
+ if (firstSource != null) {
+ packageRoot = javaProject.getPackageFragmentRoot(firstSource);
+ if (packageRoot != null) {
+ break;
+ }
+ }
+ }
+ return packageRoot;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java
index 7bc3692..c07b7be 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java
@@ -434,7 +434,7 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
* <p/>
* Capitalizes the first letter and replace non-alphabet by a space followed by a capital.
*/
- private String getUiName(String xmlName) {
+ private static String getUiName(String xmlName) {
StringBuilder sb = new StringBuilder();
boolean capitalize = true;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java
index 348e0a3..7b1f4fa 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.editors.manifest.model;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
@@ -394,7 +394,7 @@ public class UiClassAttributeNode extends UiTextAttributeNode {
if (className.startsWith(".")) { //$NON-NLS-1$
fullClassName = packageName + className;
} else {
- String[] segments = className.split(AndroidConstants.RE_DOT);
+ String[] segments = className.split(AdtConstants.RE_DOT);
if (segments.length == 1) {
fullClassName = packageName + "." + className; //$NON-NLS-1$
}
@@ -503,7 +503,7 @@ public class UiClassAttributeNode extends UiTextAttributeNode {
// look for how many segments we have left.
// if one, just write it that way.
// if more than one, write it with a leading dot.
- String[] packages = name.split(AndroidConstants.RE_DOT);
+ String[] packages = name.split(AdtConstants.RE_DOT);
if (packages.length == 1) {
text.setText(name);
} else {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiManifestElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiManifestElementNode.java
index 0b78a22..a4f701f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiManifestElementNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiManifestElementNode.java
@@ -56,7 +56,7 @@ public final class UiManifestElementNode extends UiElementNode {
/**
* Computes a short string describing the UI node suitable for tree views.
* Uses the element's attribute "android:name" if present, or the "android:label" one
- * followed by the element's name.
+ * followed by the element's name if not repeated.
*
* @return A short string describing the UI node suitable for tree views.
*/
@@ -68,12 +68,13 @@ public final class UiManifestElementNode extends UiElementNode {
manifestDescriptors = target.getManifestDescriptors();
}
+ String name = getDescriptor().getUiName();
+
if (manifestDescriptors != null &&
getXmlNode() != null &&
getXmlNode() instanceof Element &&
getXmlNode().hasAttributes()) {
-
// Application and Manifest nodes have a special treatment: they are unique nodes
// so we don't bother trying to differentiate their strings and we fall back to
// just using the UI name below.
@@ -90,12 +91,18 @@ public final class UiManifestElementNode extends UiElementNode {
AndroidManifestDescriptors.ANDROID_LABEL_ATTR);
}
if (attr != null && attr.length() > 0) {
- return String.format("%1$s (%2$s)", attr, getDescriptor().getUiName());
+ // If the ui name is repeated in the attribute value, don't use it.
+ // Typical case is to avoid ".pkg.MyActivity (Activity)".
+ if (attr.contains(name)) {
+ return attr;
+ } else {
+ return String.format("%1$s (%2$s)", attr, name);
+ }
}
}
}
- return String.format("%1$s", getDescriptor().getUiName());
+ return String.format("%1$s", name);
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewLinksPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewLinksPart.java
index 2386a8c..f821375 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewLinksPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewLinksPart.java
@@ -17,6 +17,8 @@
package com.android.ide.eclipse.adt.internal.editors.manifest.pages;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
@@ -44,7 +46,7 @@ final class OverviewLinksPart extends ManifestSectionPart {
section.setDescription("The content of the Android Manifest is made up of three sections. You can also edit the XML directly.");
Composite table = createTableLayout(toolkit, 2 /* numColumns */);
-
+
StringBuffer buf = new StringBuffer();
buf.append(String.format("<form><li style=\"image\" value=\"app_img\"><a href=\"page:%1$s\">", //$NON-NLS-1$
ApplicationPage.PAGE_ID));
@@ -67,7 +69,7 @@ final class OverviewLinksPart extends ManifestSectionPart {
buf.append(": Instrumentation defined.");
buf.append("</li>"); //$NON-NLS-1$
- buf.append(String.format("<li style=\"image\" value=\"android_img\"><a href=\"page:%1$s\">", //$NON-NLS-1$
+ buf.append(String.format("<li style=\"image\" value=\"srce_img\"><a href=\"page:%1$s\">", //$NON-NLS-1$
ManifestEditor.TEXT_EDITOR_ID));
buf.append("XML Source");
buf.append("</a>"); //$NON-NLS-1$
@@ -81,12 +83,13 @@ final class OverviewLinksPart extends ManifestSectionPart {
mFormText = createFormText(table, toolkit, true, buf.toString(),
false /* setupLayoutData */);
-
+
AndroidManifestDescriptors manifestDescriptor = editor.getManifestDescriptors();
Image androidLogo = AdtPlugin.getAndroidLogo();
mFormText.setImage("android_img", androidLogo); //$NON-NLS-1$
-
+ mFormText.setImage("srce_img", IconFactory.getInstance().getIcon(AndroidXmlEditor.ICON_XML_PAGE));
+
if (manifestDescriptor != null) {
mFormText.setImage("app_img", getIcon(manifestDescriptor.getApplicationElement())); //$NON-NLS-1$
mFormText.setImage("perm_img", getIcon(manifestDescriptor.getPermissionElement())); //$NON-NLS-1$
@@ -98,7 +101,7 @@ final class OverviewLinksPart extends ManifestSectionPart {
}
mFormText.addHyperlinkListener(editor.createHyperlinkListener());
}
-
+
/**
* Update the UI with information from the new descriptors.
* <p/>At this point, this only refreshes the icons.
@@ -114,12 +117,8 @@ final class OverviewLinksPart extends ManifestSectionPart {
mFormText.setImage("inst_img", getIcon(manifestDescriptor.getInstrumentationElement())); //$NON-NLS-1$
}
}
-
+
private Image getIcon(ElementDescriptor desc) {
- if (desc != null && desc.getIcon() != null) {
- return desc.getIcon();
- }
-
- return AdtPlugin.getAndroidLogo();
+ return desc.getCustomizedIcon();
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/MenuEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/MenuEditor.java
index 4ddb1e9..911a833 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/MenuEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/MenuEditor.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.editors.menu;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor.Mandatory;
@@ -42,7 +42,7 @@ import javax.xml.xpath.XPathExpressionException;
*/
public class MenuEditor extends AndroidXmlEditor {
- public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".menu.MenuEditor"; //$NON-NLS-1$
+ public static final String ID = AdtConstants.EDITORS_NAMESPACE + ".menu.MenuEditor"; //$NON-NLS-1$
/** Root node of the UI element hierarchy */
private UiElementNode mUiRootNode;
@@ -104,6 +104,8 @@ public class MenuEditor extends AndroidXmlEditor {
}
}
+ private boolean mUpdatingModel;
+
/**
* Processes the new XML Model, which XML root node is given.
*
@@ -111,33 +113,43 @@ public class MenuEditor extends AndroidXmlEditor {
*/
@Override
protected void xmlModelChanged(Document xml_doc) {
- // init the ui root on demand
- initUiRootNode(false /*force*/);
-
- mUiRootNode.setXmlDocument(xml_doc);
- if (xml_doc != null) {
- ElementDescriptor root_desc = mUiRootNode.getDescriptor();
- try {
- XPath xpath = AndroidXPathFactory.newXPath();
- Node node = (Node) xpath.evaluate("/" + root_desc.getXmlName(), //$NON-NLS-1$
- xml_doc,
- XPathConstants.NODE);
- if (node == null && root_desc.getMandatory() != Mandatory.NOT_MANDATORY) {
- // Create the root element if it doesn't exist yet (for empty new documents)
- node = mUiRootNode.createXmlNode();
- }
-
- // Refresh the manifest UI node and all its descendants
- mUiRootNode.loadFromXmlNode(node);
+ if (mUpdatingModel) {
+ return;
+ }
- // TODO ? startMonitoringMarkers();
- } catch (XPathExpressionException e) {
- AdtPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$
- root_desc.getXmlName());
+ try {
+ mUpdatingModel = true;
+
+ // init the ui root on demand
+ initUiRootNode(false /*force*/);
+
+ mUiRootNode.setXmlDocument(xml_doc);
+ if (xml_doc != null) {
+ ElementDescriptor root_desc = mUiRootNode.getDescriptor();
+ try {
+ XPath xpath = AndroidXPathFactory.newXPath();
+ Node node = (Node) xpath.evaluate("/" + root_desc.getXmlName(), //$NON-NLS-1$
+ xml_doc,
+ XPathConstants.NODE);
+ if (node == null && root_desc.getMandatory() != Mandatory.NOT_MANDATORY) {
+ // Create the root element if it doesn't exist yet (for empty new documents)
+ node = mUiRootNode.createXmlNode();
+ }
+
+ // Refresh the manifest UI node and all its descendants
+ mUiRootNode.loadFromXmlNode(node);
+
+ // TODO ? startMonitoringMarkers();
+ } catch (XPathExpressionException e) {
+ AdtPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$
+ root_desc.getXmlName());
+ }
}
- }
- super.xmlModelChanged(xml_doc);
+ super.xmlModelChanged(xml_doc);
+ } finally {
+ mUpdatingModel = false;
+ }
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/descriptors/MenuDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/descriptors/MenuDescriptors.java
index a84c743..f093a9b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/descriptors/MenuDescriptors.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/menu/descriptors/MenuDescriptors.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.editors.menu.descriptors;
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
import com.android.ide.common.resources.platform.DeclareStyleableInfo;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
@@ -122,7 +122,7 @@ public final class MenuDescriptors implements IDescriptorProvider {
new ElementDescriptor[] { top_item }, // childrenElements,
false /* mandatory */);
- XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_PREFIX,
+ XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_NAME,
SdkConstants.NS_RESOURCES);
updateElement(mDescriptor, styleMap, "Menu", xmlns); //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesContentAssist.java
index d8b497f..1661100 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesContentAssist.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesContentAssist.java
@@ -16,18 +16,187 @@
package com.android.ide.eclipse.adt.internal.editors.resources;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor.ATTRIBUTE_ICON_FILENAME;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.ITEM_TAG;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.NAME_ATTR;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.STYLE_ELEMENT;
+import static com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.DESCRIPTOR_LAYOUT;
+
+import com.android.annotations.VisibleForTesting;
import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import org.eclipse.jface.text.contentassist.CompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
/**
* Content Assist Processor for /res/values and /res/drawable XML files
+ * <p>
+ * Further enhancements:
+ * <ul>
+ * <li> Complete prefixes in the style element itself for the name attribute
+ * <li> Complete parent names
+ * </ul>
*/
-class ResourcesContentAssist extends AndroidContentAssist {
+@VisibleForTesting
+public class ResourcesContentAssist extends AndroidContentAssist {
/**
- * Constructor for ResourcesContentAssist
+ * Constructor for ResourcesContentAssist
*/
public ResourcesContentAssist() {
super(AndroidTargetData.DESCRIPTOR_RESOURCES);
}
+
+ @Override
+ protected void computeAttributeValues(List<ICompletionProposal> proposals, int offset,
+ String parentTagName, String attributeName, Node node, String wordPrefix,
+ boolean skipEndTag, int replaceLength) {
+ super.computeAttributeValues(proposals, offset, parentTagName, attributeName, node,
+ wordPrefix, skipEndTag, replaceLength);
+
+ if (parentTagName.equals(ITEM_TAG) && NAME_ATTR.equals(attributeName)) {
+
+ // Special case: the user is code completing inside
+ // <style><item name="^"/></style>
+ // In this case, ALL attributes are valid so we need to synthesize
+ // a choice list from all the layout descriptors
+
+ // Add in android: as a completion item?
+ if (startsWith(ANDROID_NS_NAME_PREFIX, wordPrefix)) {
+ proposals.add(new CompletionProposal(ANDROID_NS_NAME_PREFIX,
+ offset - wordPrefix.length(), // replacementOffset
+ wordPrefix.length() + replaceLength, // replacementLength
+ ANDROID_NS_NAME_PREFIX.length(), // cursorPosition
+ IconFactory.getInstance().getIcon(ATTRIBUTE_ICON_FILENAME),
+ null, null, null));
+ }
+
+
+ String attributePrefix = wordPrefix;
+ if (startsWith(attributePrefix, ANDROID_NS_NAME_PREFIX)) {
+ attributePrefix = attributePrefix.substring(ANDROID_NS_NAME_PREFIX.length());
+ }
+
+ AndroidTargetData data = mEditor.getTargetData();
+ if (data != null) {
+ IDescriptorProvider descriptorProvider =
+ data.getDescriptorProvider(
+ AndroidTargetData.DESCRIPTOR_LAYOUT);
+ if (descriptorProvider != null) {
+ ElementDescriptor[] rootElementDescriptors =
+ descriptorProvider.getRootElementDescriptors();
+ Map<String, AttributeDescriptor> matches =
+ new HashMap<String, AttributeDescriptor>(180);
+ for (ElementDescriptor elementDesc : rootElementDescriptors) {
+ for (AttributeDescriptor desc : elementDesc.getAttributes()) {
+ if (desc instanceof SeparatorAttributeDescriptor) {
+ continue;
+ }
+ String name = desc.getXmlLocalName();
+ if (startsWith(name, attributePrefix)) {
+ matches.put(name, desc);
+ }
+ }
+ }
+
+ List<AttributeDescriptor> sorted =
+ new ArrayList<AttributeDescriptor>(matches.size());
+ sorted.addAll(matches.values());
+ Collections.sort(sorted);
+ char needTag = 0;
+ addMatchingProposals(proposals, sorted.toArray(), offset, node, wordPrefix,
+ needTag, true /* isAttribute */, false /* isNew */,
+ skipEndTag /* skipEndTag */, replaceLength);
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void computeTextValues(List<ICompletionProposal> proposals, int offset,
+ Node parentNode, Node currentNode, UiElementNode uiParent,
+ String prefix) {
+ super.computeTextValues(proposals, offset, parentNode, currentNode, uiParent,
+ prefix);
+
+ if (parentNode.getNodeName().equals(ITEM_TAG) &&
+ parentNode.getParentNode() != null &&
+ STYLE_ELEMENT.equals(parentNode.getParentNode().getNodeName())) {
+
+ // Special case: the user is code completing inside
+ // <style><item name="android:foo"/>|</style>
+ // In this case, we need to find the right AttributeDescriptor
+ // for the given attribute and offer its values
+
+ AndroidTargetData data = mEditor.getTargetData();
+ if (data != null) {
+ IDescriptorProvider descriptorProvider =
+ data.getDescriptorProvider(DESCRIPTOR_LAYOUT);
+ if (descriptorProvider != null) {
+
+ Element element = (Element) parentNode;
+ String attrName = element.getAttribute(NAME_ATTR);
+ int pos = attrName.indexOf(':');
+ if (pos >= 0) {
+ attrName = attrName.substring(pos + 1);
+ }
+
+ // Search for an attribute match
+ ElementDescriptor[] rootElementDescriptors =
+ descriptorProvider.getRootElementDescriptors();
+ for (ElementDescriptor elementDesc : rootElementDescriptors) {
+ for (AttributeDescriptor desc : elementDesc.getAttributes()) {
+ if (desc.getXmlLocalName().equals(attrName)) {
+ // Make a ui parent node such that we can attach our
+ // newfound attribute node to something (the code we delegate
+ // to for looking up attribute completions will look at the
+ // parent node and ask for its editor etc.)
+ if (uiParent == null) {
+ DocumentDescriptor documentDescriptor =
+ data.getLayoutDescriptors().getDescriptor();
+ uiParent = documentDescriptor.createUiNode();
+ uiParent.setEditor(mEditor);
+ }
+
+ UiAttributeNode currAttrNode = desc.createUiNode(uiParent);
+ AttribInfo attrInfo = new AttribInfo();
+ Object[] values = getAttributeValueChoices(currAttrNode, attrInfo,
+ prefix);
+ char needTag = attrInfo.needTag;
+ if (attrInfo.correctedPrefix != null) {
+ prefix = attrInfo.correctedPrefix;
+ }
+ boolean isAttribute = true;
+ boolean isNew = false;
+ int replaceLength = computeTextReplaceLength(currentNode, offset);
+ addMatchingProposals(proposals, values, offset, currentNode,
+ prefix, needTag, isAttribute, isNew,
+ false /* skipEndTag */, replaceLength);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesEditor.java
index 79d11ed..83e2d8c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesEditor.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.editors.resources;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors;
@@ -38,11 +38,11 @@ import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
/**
- * Multi-page form editor for /res/values and /res/drawable XML files.
+ * Multi-page form editor for /res/values XML files.
*/
public class ResourcesEditor extends AndroidXmlEditor {
- public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".resources.ResourcesEditor"; //$NON-NLS-1$
+ public static final String ID = AdtConstants.EDITORS_NAMESPACE + ".resources.ResourcesEditor"; //$NON-NLS-1$
/** Root node of the UI element hierarchy */
private UiElementNode mUiResourcesNode;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesTreePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesTreePage.java
index 2c383b3..f352cfa 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesTreePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/ResourcesTreePage.java
@@ -16,12 +16,12 @@
package com.android.ide.eclipse.adt.internal.editors.resources;
+import com.android.ide.common.resources.ResourceFolder;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IPageImageProvider;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.ui.tree.UiTreeBlock;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import org.eclipse.core.resources.IFile;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java
index 721e093..6a2368a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/descriptors/ResourcesDescriptors.java
@@ -35,12 +35,14 @@ public final class ResourcesDescriptors implements IDescriptorProvider {
// Public attributes names, attributes descriptors and elements descriptors
- public static final String ROOT_ELEMENT = "resources"; //$NON-NLS-1$
+ public static final String ROOT_ELEMENT = "resources"; //$NON-NLS-1$
public static final String STRING_ELEMENT = "string"; //$NON-NLS-1$
+ public static final String STYLE_ELEMENT = "style"; //$NON-NLS-1$
- public static final String ITEM_TAG = "item"; //$NON-NLS-1$
+ public static final String ITEM_TAG = "item"; //$NON-NLS-1$
public static final String NAME_ATTR = "name"; //$NON-NLS-1$
public static final String TYPE_ATTR = "type"; //$NON-NLS-1$
+ public static final String PARENT_ATTR = "parent"; //$NON-NLS-1$
private static final ResourcesDescriptors sThis = new ResourcesDescriptors();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/DecorComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/DecorComposite.java
index 96539fa..4edb09f 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/DecorComposite.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/DecorComposite.java
@@ -74,6 +74,8 @@ public class DecorComposite extends Composite {
setImage(i);
}
+ content.createToolbarItems(mToolbar);
+
return this;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/IDecorContent.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/IDecorContent.java
index eb06a1b..6f14165 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/IDecorContent.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/IDecorContent.java
@@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.editors.ui;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.ToolBar;
/**
* Describes the content of a {@link IDecorContent}.
@@ -32,6 +33,16 @@ public interface IDecorContent {
public void createControl(Composite parent);
/**
+ * Creates the toolbar items that will be displayed in the {@link IDecorContent}. This
+ * method will always be called <b>after</b> {@link #createControl}, so the
+ * implementation can assume that it can call {@link #getControl()} to obtain the
+ * corresponding control that the toolbar items will operate on.
+ *
+ * @param toolbar the toolbar to add the toolbar items to
+ */
+ public void createToolbarItems(ToolBar toolbar);
+
+ /**
* Returns the control previously created by {@link #createControl(Composite)}.
* @return A control to display in the {@link IDecorContent}. Must not be null.
*/
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java
index 593d657..2eca501 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java
@@ -268,7 +268,7 @@ class UiElementDetail implements IDetailsPage {
FormText text = SectionHelper.createFormText(masterTable, toolkit,
true /* isHtml */, tooltip, true /* setupLayoutData */);
text.addHyperlinkListener(mTree.getEditor().createHyperlinkListener());
- Image icon = elem_desc.getIcon();
+ Image icon = elem_desc.getCustomizedIcon();
if (icon != null) {
text.setImage(DescriptorsUtils.IMAGE_KEY, icon);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java
index 46b0a6b..451a6eb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java
@@ -29,7 +29,7 @@ import org.eclipse.swt.graphics.Image;
* UiModelTreeLabelProvider is a trivial implementation of {@link ILabelProvider}
* where elements are expected to derive from {@link UiElementNode} or
* from {@link ElementDescriptor}.
- *
+ *
* It is used by both the master tree viewer and by the list in the Add... selection dialog.
*/
public class UiModelTreeLabelProvider implements ILabelProvider {
@@ -42,26 +42,27 @@ public class UiModelTreeLabelProvider implements ILabelProvider {
*/
public Image getImage(Object element) {
ElementDescriptor desc = null;
+ UiElementNode node = null;
+
if (element instanceof ElementDescriptor) {
- Image img = ((ElementDescriptor) element).getIcon();
- if (img != null) {
- return img;
- }
+ desc = (ElementDescriptor) element;
} else if (element instanceof UiElementNode) {
- UiElementNode node = (UiElementNode) element;
+ node = (UiElementNode) element;
desc = node.getDescriptor();
- if (desc != null) {
- Image img = desc.getIcon();
- if (img != null) {
- if (node.hasError()) {
- //TODO: cache image
- return new ErrorImageComposite(img).createImage();
- } else {
- return img;
- }
+ }
+
+ if (desc != null) {
+ Image img = desc.getCustomizedIcon();
+ if (img != null) {
+ if (node != null && node.hasError()) {
+ //TODO: cache image
+ return new ErrorImageComposite(img).createImage();
+ } else {
+ return img;
}
}
}
+
return AdtPlugin.getAndroidLogo();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java
index 596cfe3..9c822f6 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.editors.uimodel;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import org.eclipse.swt.widgets.Composite;
@@ -38,8 +39,8 @@ public abstract class UiAttributeNode {
private boolean mIsDirty;
private boolean mHasError;
- /** Creates a new {@link UiAttributeNode} linked to a specific {@link AttributeDescriptor}
- * and the corresponding runtine {@link UiElementNode} parent. */
+ /** Creates a new {@link UiAttributeNode} linked to a specific {@link AttributeDescriptor}
+ * and the corresponding runtime {@link UiElementNode} parent. */
public UiAttributeNode(AttributeDescriptor attributeDescriptor, UiElementNode uiParent) {
mDescriptor = attributeDescriptor;
mUiParent = uiParent;
@@ -54,7 +55,7 @@ public abstract class UiAttributeNode {
public final UiElementNode getUiParent() {
return mUiParent;
}
-
+
/** Returns the current value of the node. */
public abstract String getCurrentValue();
@@ -78,10 +79,13 @@ public abstract class UiAttributeNode {
mIsDirty = isDirty;
// TODO: for unknown attributes, getParent() != null && getParent().getEditor() != null
if (old_value != isDirty) {
- getUiParent().getEditor().editorDirtyStateChanged();
+ AndroidXmlEditor editor = getUiParent().getEditor();
+ if (editor != null) {
+ editor.editorDirtyStateChanged();
+ }
}
}
-
+
/**
* Sets the error flag value.
* @param errorFlag the error flag
@@ -89,21 +93,21 @@ public abstract class UiAttributeNode {
public final void setHasError(boolean errorFlag) {
mHasError = errorFlag;
}
-
+
/**
* Returns whether this node has errors.
*/
public final boolean hasError() {
return mHasError;
}
-
+
/**
* Called once by the parent user interface to creates the necessary
* user interface to edit this attribute.
* <p/>
* This method can be called more than once in the life cycle of an UI node,
* typically when the UI is part of a master-detail tree, as pages are swapped.
- *
+ *
* @param parent The composite where to create the user interface.
* @param managedForm The managed form owning this part.
*/
@@ -116,7 +120,7 @@ public abstract class UiAttributeNode {
* for an attribute.
* <p/>
* Implementations that do not have any known values should return null.
- *
+ *
* @param prefix An optional prefix string, which is whatever the user has already started
* typing. Can be null or an empty string. The implementation can use this to filter choices
* and only return strings that match this prefix. A lazy or default implementation can
@@ -136,7 +140,7 @@ public abstract class UiAttributeNode {
* The caller doesn't really know if attributes have changed,
* so it will call this to refresh the attribute anyway. It's up to the
* UI implementation to minimize refreshes.
- *
+ *
* @param xml_attribute_node
*/
public abstract void updateValue(Node xml_attribute_node);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
index 36cfc80..93b6baf 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
@@ -16,13 +16,14 @@
package com.android.ide.eclipse.adt.internal.editors.uimodel;
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS;
import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS_URI;
import static com.android.sdklib.SdkConstants.NS_RESOURCES;
+import com.android.annotations.VisibleForTesting;
import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.common.resources.platform.AttributeInfo;
import com.android.ide.eclipse.adt.AdtPlugin;
@@ -210,17 +211,24 @@ public class UiElementNode implements IPropertySource {
/**
* Computes a short string describing the UI node suitable for tree views.
* Uses the element's attribute "android:name" if present, or the "android:label" one
- * followed by the element's name.
+ * followed by the element's name if not repeated.
*
* @return A short string describing the UI node suitable for tree views.
*/
public String getShortDescription() {
+ String name = mDescriptor.getUiName();
String attr = getDescAttribute();
if (attr != null) {
- return String.format("%1$s (%2$s)", attr, mDescriptor.getUiName());
+ // If the ui name is repeated in the attribute value, don't use it.
+ // Typical case is to avoid ".pkg.MyActivity (Activity)".
+ if (attr.contains(name)) {
+ return attr;
+ } else {
+ return String.format("%1$s (%2$s)", attr, name);
+ }
}
- return mDescriptor.getUiName();
+ return name;
}
/** Returns the key attribute that can be used to describe this node, or null */
@@ -1436,8 +1444,8 @@ public class UiElementNode implements IPropertySource {
// --- for derived implementations only ---
- // TODO doc
- protected void setXmlNode(Node xmlNode) {
+ @VisibleForTesting
+ public void setXmlNode(Node xmlNode) {
mXmlNode = xmlNode;
}
@@ -1538,7 +1546,7 @@ public class UiElementNode implements IPropertySource {
* e.g. SdkConstants.NS_RESOURCES
* @return The first prefix declared or the default "android" prefix.
*/
- private String lookupNamespacePrefix(Node node, String nsUri) {
+ public static String lookupNamespacePrefix(Node node, String nsUri) {
// Note: Node.lookupPrefix is not implemented in wst/xml/core NodeImpl.java
// The following code emulates this simple call:
// String prefix = node.lookupPrefix(SdkConstants.NS_RESOURCES);
@@ -1591,7 +1599,7 @@ public class UiElementNode implements IPropertySource {
// We need to make sure the prefix is not one that was declared in the scope
// visited above. Use a default namespace prefix "android" for the Android resource
// NS and use "ns" for all other custom namespaces.
- String prefix = NS_RESOURCES.equals(nsUri) ? ANDROID_NS_PREFIX : "ns"; //$NON-NLS-1$
+ String prefix = NS_RESOURCES.equals(nsUri) ? ANDROID_NS_NAME : "ns"; //$NON-NLS-1$
String base = prefix;
for (int i = 1; visited.contains(prefix); i++) {
prefix = base + Integer.toString(i);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiListAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiListAttributeNode.java
index cdb0c9a..c8e5720 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiListAttributeNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiListAttributeNode.java
@@ -140,7 +140,7 @@ public class UiListAttributeNode extends UiAbstractTextAttributeNode {
// FrameworkResourceManager expects a specific prefix for the attribute.
String nsPrefix = "";
if (SdkConstants.NS_RESOURCES.equals(descriptor.getNamespaceUri())) {
- nsPrefix = LayoutConstants.ANDROID_NS_PREFIX + ':';
+ nsPrefix = LayoutConstants.ANDROID_NS_NAME + ':';
} else if (XmlnsAttributeDescriptor.XMLNS_URI.equals(descriptor.getNamespaceUri())) {
nsPrefix = XmlnsAttributeDescriptor.XMLNS_COLON;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java
index 32e6167..2b7f346 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java
@@ -16,13 +16,21 @@
package com.android.ide.eclipse.adt.internal.editors.uimodel;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_STYLE;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_RESOURCE_REF;
+import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
+
+import com.android.ide.common.api.IAttributeInfo;
+import com.android.ide.common.api.IAttributeInfo.Format;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
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.ui.ReferenceChooserDialog;
@@ -46,6 +54,11 @@ import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.TableWrapData;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -59,7 +72,6 @@ import java.util.regex.Pattern;
* See {@link UiTextAttributeNode} for more information.
*/
public class UiResourceAttributeNode extends UiTextAttributeNode {
-
private ResourceType mType;
public UiResourceAttributeNode(ResourceType type,
@@ -126,19 +138,19 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
IProject project = editor.getProject();
if (project != null) {
// get the resource repository for this project and the system resources.
- IResourceRepository projectRepository =
+ ResourceRepository projectRepository =
ResourceManager.getInstance().getProjectResources(project);
if (mType != null) {
// get the Target Data to get the system resources
AndroidTargetData data = editor.getTargetData();
- IResourceRepository systemRepository = data.getSystemResources();
+ ResourceRepository frameworkRepository = data.getFrameworkResources();
// open a resource chooser dialog for specified resource type.
ResourceChooser dlg = new ResourceChooser(project,
mType,
projectRepository,
- systemRepository,
+ frameworkRepository,
shell);
dlg.setCurrentResource(currentValue);
@@ -194,13 +206,15 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
*/
@Override
public String[] getPossibleValues(String prefix) {
- IResourceRepository repository = null;
- boolean isSystem = false;
+ return computeResourceStringMatches(getUiParent().getEditor(), getDescriptor(), prefix);
+ }
- UiElementNode uiNode = getUiParent();
- AndroidXmlEditor editor = uiNode.getEditor();
+ public static String[] computeResourceStringMatches(AndroidXmlEditor editor,
+ AttributeDescriptor attributeDescriptor, String prefix) {
+ ResourceRepository repository = null;
+ boolean isSystem = false;
- if (prefix == null || prefix.indexOf("android:") < 0) { //$NON-NLS-1$
+ if (prefix == null || !prefix.regionMatches(1, ANDROID_PKG, 0, ANDROID_PKG.length())) {
IProject project = editor.getProject();
if (project != null) {
// get the resource repository for this project and the system resources.
@@ -208,18 +222,17 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
}
} else {
// If there's a prefix with "android:" in it, use the system resources
- //
- // TODO find a way to only list *public* framework resources here.
+ // Non-public framework resources are filtered out later.
AndroidTargetData data = editor.getTargetData();
- repository = data.getSystemResources();
+ repository = data.getFrameworkResources();
isSystem = true;
}
// Get list of potential resource types, either specific to this project
// or the generic list.
- ResourceType[] resTypes = (repository != null) ?
+ Collection<ResourceType> resTypes = (repository != null) ?
repository.getAvailableResourceTypes() :
- ResourceType.values();
+ EnumSet.allOf(ResourceType.class);
// Get the type name from the prefix, if any. It's any word before the / if there's one
String typeName = null;
@@ -231,7 +244,7 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
}
// Now collect results
- ArrayList<String> results = new ArrayList<String>();
+ List<String> results = new ArrayList<String>();
if (typeName == null) {
// This prefix does not have a / in it, so the resource string is either empty
@@ -239,12 +252,23 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
// resource types.
for (ResourceType resType : resTypes) {
- results.add("@" + resType.getName() + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (isSystem) {
+ results.add(PREFIX_ANDROID_RESOURCE_REF + resType.getName() + '/');
+ } else {
+ results.add('@' + resType.getName() + '/');
+ }
if (resType == ResourceType.ID) {
// Also offer the + version to create an id from scratch
- results.add("@+" + resType.getName() + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ results.add("@+" + resType.getName() + '/'); //$NON-NLS-1$
}
}
+
+ // Also add in @android: prefix to completion such that if user has typed
+ // "@an" we offer to complete it.
+ if (prefix == null ||
+ ANDROID_PKG.regionMatches(0, prefix, 1, prefix.length() - 1)) {
+ results.add(PREFIX_ANDROID_RESOURCE_REF);
+ }
} else if (repository != null) {
// We have a style name and a repository. Find all resources that match this
// type and recreate suggestions out of them.
@@ -258,18 +282,112 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
}
if (isSystem) {
- sb.append("android:"); //$NON-NLS-1$
+ sb.append(ANDROID_PKG).append(':');
}
sb.append(typeName).append('/');
String base = sb.toString();
- for (ResourceItem item : repository.getResources(resType)) {
+ for (ResourceItem item : repository.getResourceItemsOfType(resType)) {
results.add(base + item.getName());
}
}
}
+ if (attributeDescriptor != null) {
+ sortAttributeChoices(attributeDescriptor, results);
+ } else {
+ Collections.sort(results);
+ }
+
return results.toArray(new String[results.size()]);
}
+
+ /**
+ * Attempts to sort the attribute values to bubble up the most likely choices to
+ * the top.
+ * <p>
+ * For example, if you are editing a style attribute, it's likely that among the
+ * resource values you would rather see @style or @android than @string.
+ */
+ private static void sortAttributeChoices(AttributeDescriptor descriptor,
+ List<String> choices) {
+ final IAttributeInfo attributeInfo = descriptor.getAttributeInfo();
+ Collections.sort(choices, new Comparator<String>() {
+ public int compare(String s1, String s2) {
+ int compare = score(attributeInfo, s1) - score(attributeInfo, s2);
+ if (compare == 0) {
+ // Sort alphabetically as a fallback
+ compare = s1.compareTo(s2);
+ }
+ return compare;
+ }
+ });
+ }
+
+ /** Compute a suitable sorting score for the given */
+ private static final int score(IAttributeInfo attributeInfo, String value) {
+ if (value.equals(PREFIX_ANDROID_RESOURCE_REF)) {
+ return -1;
+ }
+
+ for (Format format : attributeInfo.getFormats()) {
+ String type = null;
+ switch (format) {
+ case BOOLEAN:
+ type = "bool"; //$NON-NLS-1$
+ break;
+ case COLOR:
+ type = "color"; //$NON-NLS-1$
+ break;
+ case DIMENSION:
+ type = "dimen"; //$NON-NLS-1$
+ break;
+ case INTEGER:
+ type = "integer"; //$NON-NLS-1$
+ break;
+ case STRING:
+ type = "string"; //$NON-NLS-1$
+ break;
+ // default: REFERENCE, FLAG, ENUM, etc - don't have type info about individual
+ // elements to help make a decision
+ }
+
+ if (type != null) {
+ if (value.startsWith('@' + type + '/')) {
+ return -2;
+ }
+
+ if (value.startsWith(PREFIX_ANDROID_RESOURCE_REF + type + '/')) {
+ return -2;
+ }
+ }
+ }
+
+ // Handle a few more cases not covered by the Format metadata check
+ String type = null;
+
+ String attribute = attributeInfo.getName();
+ if (attribute.equals(ATTR_ID)) {
+ type = "id"; //$NON-NLS-1$
+ } else if (attribute.equals(ATTR_STYLE)) {
+ type = "style"; //$NON-NLS-1$
+ } else if (attribute.equals(LayoutDescriptors.ATTR_LAYOUT)) {
+ type = "layout"; //$NON-NLS-1$
+ } else if (attribute.equals("drawable")) { //$NON-NLS-1$
+ type = "drawable"; //$NON-NLS-1$
+ }
+
+ if (type != null) {
+ if (value.startsWith('@' + type + '/')) {
+ return -2;
+ }
+
+ if (value.startsWith(PREFIX_ANDROID_RESOURCE_REF + type + '/')) {
+ return -2;
+ }
+ }
+
+ return 0;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java
index 29545d5..83e9bc4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java
@@ -21,40 +21,47 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_CLASS;
import static com.android.ide.common.layout.LayoutConstants.ATTR_ON_CLICK;
import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.VIEW;
-import static com.android.ide.eclipse.adt.AndroidConstants.ANDROID_PKG;
-import static com.android.ide.eclipse.adt.AndroidConstants.EXT_XML;
-import static com.android.ide.eclipse.adt.AndroidConstants.FN_RESOURCE_BASE;
-import static com.android.ide.eclipse.adt.AndroidConstants.FN_RESOURCE_CLASS;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_RESOURCE_REF;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF;
+import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.FN_RESOURCE_BASE;
+import static com.android.ide.eclipse.adt.AdtConstants.FN_RESOURCE_CLASS;
import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.NAME_ATTR;
import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.ROOT_ELEMENT;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.STYLE_ELEMENT;
+import static com.android.sdklib.SdkConstants.FD_DOCS;
+import static com.android.sdklib.SdkConstants.FD_DOCS_REFERENCE;
import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_NAME;
import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_PACKAGE;
import static com.android.sdklib.xml.AndroidManifest.NODE_ACTIVITY;
import static com.android.sdklib.xml.AndroidManifest.NODE_SERVICE;
import com.android.annotations.VisibleForTesting;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceFolder;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.manager.FolderTypeRelationship;
+import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
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.Sdk;
import com.android.ide.eclipse.adt.io.IFileWrapper;
import com.android.ide.eclipse.adt.io.IFolderWrapper;
+import com.android.io.FileWrapper;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.FileWrapper;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.IAbstractFolder;
import com.android.util.Pair;
import org.apache.xerces.parsers.DOMParser;
@@ -83,7 +90,6 @@ import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
@@ -108,6 +114,7 @@ import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
@@ -139,6 +146,8 @@ import org.xml.sax.SAXException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -153,6 +162,14 @@ import java.util.regex.Pattern;
*/
@SuppressWarnings("restriction")
public class Hyperlinks {
+ private static final String CATEGORY = "category"; //$NON-NLS-1$
+ private static final String ACTION = "action"; //$NON-NLS-1$
+ private static final String PERMISSION = "permission"; //$NON-NLS-1$
+ private static final String USES_PERMISSION = "uses-permission"; //$NON-NLS-1$
+ private static final String CATEGORY_PKG_PREFIX = "android.intent.category."; //$NON-NLS-1$
+ private static final String ACTION_PKG_PREFIX = "android.intent.action."; //$NON-NLS-1$
+ private static final String PERMISSION_PKG_PREFIX = "android.permission."; //$NON-NLS-1$
+
private Hyperlinks() {
// Not instantiatable. This is a container class containing shared code
// for the various inner classes that are actual hyperlink resolvers.
@@ -164,7 +181,7 @@ public class Hyperlinks {
"(([a-zA-Z_\\$][a-zA-Z0-9_\\$]*)+\\.)+[a-zA-Z_\\$][a-zA-Z0-9_\\$]*"); //$NON-NLS-1$
/** Determines whether the given attribute <b>name</b> is linkable */
- private static boolean isAttributeNameLink(@SuppressWarnings("unused") XmlContext context) {
+ private static boolean isAttributeNameLink(XmlContext context) {
// We could potentially allow you to link to builtin Android properties:
// ANDROID_URI.equals(attribute.getNamespaceURI())
// and then jump into the res/values/attrs.xml document that is available
@@ -187,8 +204,8 @@ public class Hyperlinks {
return false;
}
- if (isClassAttribute(context) || isOnClickAttribute(context) || isActivity(context)
- || isService(context)) {
+ if (isClassAttribute(context) || isOnClickAttribute(context)
+ || isManifestName(context) || isStyleAttribute(context)) {
return true;
}
@@ -200,7 +217,7 @@ public class Hyperlinks {
return false;
}
- Pair<ResourceType,String> resource = parseResource(value);
+ Pair<ResourceType,String> resource = ResourceHelper.parseResource(value);
if (resource != null) {
ResourceType type = resource.getFirst();
if (type != null) {
@@ -237,6 +254,79 @@ public class Hyperlinks {
return false;
}
+ /**
+ * Returns true if this node/attribute pair corresponds to a manifest android:name reference
+ */
+ private static boolean isManifestName(XmlContext context) {
+ Attr attribute = context.getAttribute();
+ if (attribute != null && ATTRIBUTE_NAME.equals(attribute.getLocalName())
+ && ANDROID_URI.equals(attribute.getNamespaceURI())) {
+ if (getEditor() instanceof ManifestEditor) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Opens the declaration corresponding to an android:name reference in the
+ * AndroidManifest.xml file
+ */
+ private static boolean openManifestName(IProject project, XmlContext context) {
+ if (isActivity(context)) {
+ String fqcn = getActivityClassFqcn(context);
+ return AdtPlugin.openJavaClass(project, fqcn);
+ } else if (isService(context)) {
+ String fqcn = getServiceClassFqcn(context);
+ return AdtPlugin.openJavaClass(project, fqcn);
+ } else if (isBuiltinPermission(context)) {
+ String permission = context.getAttribute().getValue();
+ // Mutate something like android.permission.ACCESS_CHECKIN_PROPERTIES
+ // into relative doc url android/Manifest.permission.html#ACCESS_CHECKIN_PROPERTIES
+ assert permission.startsWith(PERMISSION_PKG_PREFIX);
+ String relative = "android/Manifest.permission.html#" //$NON-NLS-1$
+ + permission.substring(PERMISSION_PKG_PREFIX.length());
+
+ URL url = getDocUrl(relative);
+ if (url != null) {
+ AdtPlugin.openUrl(url);
+ return true;
+ } else {
+ return false;
+ }
+ } else if (isBuiltinIntent(context)) {
+ String intent = context.getAttribute().getValue();
+ // Mutate something like android.intent.action.MAIN into
+ // into relative doc url android/content/Intent.html#ACTION_MAIN
+ String relative;
+ if (intent.startsWith(ACTION_PKG_PREFIX)) {
+ relative = "android/content/Intent.html#ACTION_" //$NON-NLS-1$
+ + intent.substring(ACTION_PKG_PREFIX.length());
+ } else if (intent.startsWith(CATEGORY_PKG_PREFIX)) {
+ relative = "android/content/Intent.html#CATEGORY_" //$NON-NLS-1$
+ + intent.substring(CATEGORY_PKG_PREFIX.length());
+ } else {
+ return false;
+ }
+ URL url = getDocUrl(relative);
+ if (url != null) {
+ AdtPlugin.openUrl(url);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ /** Returns true if this represents a {@code <view class="foo.bar.Baz">} class attribute */
+ private static boolean isStyleAttribute(XmlContext context) {
+ String tag = context.getElement().getTagName();
+ return STYLE_ELEMENT.equals(tag);
+ }
+
/** Returns true if this represents a {@code <view class="foo.bar.Baz">} class attribute */
private static boolean isClassAttribute(XmlContext context) {
Attr attribute = context.getAttribute();
@@ -298,6 +388,72 @@ public class Hyperlinks {
}
/**
+ * Returns a URL pointing to the Android reference documentation, either installed
+ * locally or the one on android.com
+ *
+ * @param relative a relative url to append to the root url
+ * @return a URL pointing to the documentation
+ */
+ private static URL getDocUrl(String relative) {
+ // First try to find locally installed documentation
+ File sdkLocation = new File(Sdk.getCurrent().getSdkLocation());
+ File docs = new File(sdkLocation, FD_DOCS + File.separator + FD_DOCS_REFERENCE);
+ try {
+ if (docs.exists()) {
+ String s = docs.toURI().toURL().toExternalForm();
+ if (!s.endsWith("/")) { //$NON-NLS-1$
+ s += "/"; //$NON-NLS-1$
+ }
+ return new URL(s + relative);
+ }
+ // If not, fallback to the online documentation
+ return new URL("http://developer.android.com/reference/" + relative); //$NON-NLS-1$
+ } catch (MalformedURLException e) {
+ AdtPlugin.log(e, "Can't create URL for %1$s", docs);
+ return null;
+ }
+ }
+
+ /** Returns true if the context is pointing to a permission name reference */
+ private static boolean isBuiltinPermission(XmlContext context) {
+ Attr attribute = context.getAttribute();
+ Element node = context.getElement();
+
+ // Is this an <activity> or <service> in an AndroidManifest.xml file? If so, jump to it
+ String nodeName = node.getNodeName();
+ if ((USES_PERMISSION.equals(nodeName) || PERMISSION.equals(nodeName))
+ && ATTRIBUTE_NAME.equals(attribute.getLocalName())
+ && ANDROID_URI.equals(attribute.getNamespaceURI())) {
+ String value = attribute.getValue();
+ if (value.startsWith(PERMISSION_PKG_PREFIX)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** Returns true if the context is pointing to an intent reference */
+ private static boolean isBuiltinIntent(XmlContext context) {
+ Attr attribute = context.getAttribute();
+ Element node = context.getElement();
+
+ // Is this an <activity> or <service> in an AndroidManifest.xml file? If so, jump to it
+ String nodeName = node.getNodeName();
+ if ((ACTION.equals(nodeName) || CATEGORY.equals(nodeName))
+ && ATTRIBUTE_NAME.equals(attribute.getLocalName())
+ && ANDROID_URI.equals(attribute.getNamespaceURI())) {
+ String value = attribute.getValue();
+ if (value.startsWith(ACTION_PKG_PREFIX) || value.startsWith(CATEGORY_PKG_PREFIX)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
* Returns the fully qualified class name of an activity referenced by the given
* AndroidManifest.xml node
*/
@@ -307,8 +463,17 @@ public class Hyperlinks {
StringBuilder sb = new StringBuilder();
Element root = node.getOwnerDocument().getDocumentElement();
String pkg = root.getAttribute(ATTRIBUTE_PACKAGE);
- sb.append(pkg);
String className = attribute.getValue();
+ if (className.startsWith(".")) { //$NON-NLS-1$
+ sb.append(pkg);
+ } else if (className.indexOf('.') == -1) {
+ // According to the <activity> manifest element documentation, this is not
+ // valid ( http://developer.android.com/guide/topics/manifest/activity-element.html )
+ // but it appears in manifest files and appears to be supported by the runtime
+ // so handle this in code as well:
+ sb.append(pkg);
+ sb.append('.');
+ } // else: the class name is already a fully qualified class name
sb.append(className);
return sb.toString();
}
@@ -323,24 +488,6 @@ public class Hyperlinks {
}
/**
- * Is this a resource that can be defined in any file within the "values" folder?
- *
- * @param type the resource type to check
- * @return true if the given resource type can be represented as a value under the
- * values/ folder
- */
- public static boolean isValueResource(ResourceType type) {
- ResourceFolderType[] folderTypes = FolderTypeRelationship.getRelatedFolders(type);
- for (ResourceFolderType folderType : folderTypes) {
- if (folderType == ResourceFolderType.VALUES) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
* Returns the XML tag containing an element description for value items of the given
* resource type
*
@@ -369,14 +516,10 @@ public class Hyperlinks {
return false;
}
- if (isActivity(context)) {
- String fqcn = getActivityClassFqcn(context);
- return openJavaClass(project, fqcn);
- } else if (isService(context)) {
- String fqcn = getServiceClassFqcn(context);
- return openJavaClass(project, fqcn);
+ if (isManifestName(context)) {
+ return openManifestName(project, context);
} else if (isClassElement(context) || isClassAttribute(context)) {
- return openJavaClass(project, getClassFqcn(context));
+ return AdtPlugin.openJavaClass(project, getClassFqcn(context));
} else if (isOnClickAttribute(context)) {
return openOnClickMethod(project, context.getAttribute().getValue());
} else {
@@ -384,21 +527,6 @@ public class Hyperlinks {
}
}
- /** Opens the given file and shows the given (optional) region */
- private static void openFile(IFile file, IRegion region) throws PartInitException {
- IEditorPart sourceEditor = getEditor();
- IWorkbenchPage page = sourceEditor.getEditorSite().getPage();
- IEditorPart targetEditor = IDE.openEditor(page, file, true);
- if (targetEditor instanceof AndroidXmlEditor) {
- AndroidXmlEditor editor = (AndroidXmlEditor) targetEditor;
- if ((region != null)) {
- editor.show(region.getOffset(), region.getLength());
- } else {
- editor.setActivePage(AndroidXmlEditor.TEXT_EDITOR_ID);
- }
- }
- }
-
/** Opens a path (which may not be in the workspace) */
private static void openPath(IPath filePath, IRegion region, int offset) {
IEditorPart sourceEditor = getEditor();
@@ -410,7 +538,7 @@ public class Hyperlinks {
IResource file = workspace.findMember(relativePath);
if (file instanceof IFile) {
try {
- openFile((IFile)file, region);
+ AdtPlugin.openFile((IFile) file, region);
return;
} catch (PartInitException ex) {
AdtPlugin.log(ex, "Can't open %$1s", filePath); //$NON-NLS-1$
@@ -459,38 +587,6 @@ public class Hyperlinks {
}
/**
- * Opens a Java class for the given fully qualified class name
- *
- * @param project the project containing the class
- * @param fqcn the fully qualified class name of the class to be opened
- * @return true if the class was opened, false otherwise
- */
- public static boolean openJavaClass(IProject project, String fqcn) {
- if (fqcn == null) {
- return false;
- }
-
- // Handle inner classes
- if (fqcn.indexOf('$') != -1) {
- fqcn = fqcn.replaceAll("\\$", "."); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- try {
- if (project.hasNature(JavaCore.NATURE_ID)) {
- IJavaProject javaProject = JavaCore.create(project);
- IJavaElement result = javaProject.findType(fqcn);
- if (result != null) {
- return JavaUI.openInEditor(result) != null;
- }
- }
- } catch (Throwable e) {
- AdtPlugin.log(e, "Can't open class %1$s", fqcn); //$NON-NLS-1$
- }
-
- return false;
- }
-
- /**
* Opens a Java method referenced by the given on click attribute method name
*
* @param project the project containing the click handler
@@ -525,7 +621,7 @@ public class Hyperlinks {
try {
IJavaSearchScope scope = null;
IType activityType = null;
- IJavaProject javaProject = (IJavaProject) project.getNature(JavaCore.NATURE_ID);
+ IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
if (javaProject != null) {
activityType = javaProject.findType(SdkConstants.CLASS_ACTIVITY);
if (activityType != null) {
@@ -590,10 +686,11 @@ public class Hyperlinks {
}
// Create a configuration from the current file
+ IProject project = null;
IEditorInput editorInput = editor.getEditorInput();
if (editorInput instanceof FileEditorInput) {
IFile file = ((FileEditorInput) editorInput).getFile();
- IProject project = file.getProject();
+ project = file.getProject();
ProjectResources pr = ResourceManager.getInstance().getProjectResources(project);
IContainer parent = file.getParent();
if (parent instanceof IFolder) {
@@ -603,6 +700,22 @@ public class Hyperlinks {
}
}
}
+
+ // Might be editing a Java file, where there is no configuration context.
+ // Instead look at surrounding files in the workspace and obtain one valid
+ // configuration.
+ for (IEditorReference reference : editor.getSite().getPage().getEditorReferences()) {
+ IEditorPart part = reference.getEditor(false /*restore*/);
+ if (part instanceof LayoutEditor) {
+ LayoutEditor layoutEditor = (LayoutEditor) part;
+ if (project == null || layoutEditor.getProject() == project) {
+ GraphicalEditorPart graphicalEditor = layoutEditor.getGraphicalEditor();
+ if (graphicalEditor != null) {
+ return graphicalEditor.getConfiguration();
+ }
+ }
+ }
+ }
}
return null;
@@ -630,7 +743,7 @@ public class Hyperlinks {
}
/** Return either the project resources or the framework resources (or null) */
- private static ProjectResources getResources(IProject project, boolean framework) {
+ private static ResourceRepository getResources(IProject project, boolean framework) {
if (framework) {
IAndroidTarget target = getTarget(project);
if (target == null) {
@@ -662,7 +775,7 @@ public class Hyperlinks {
}
// Look in the configuration folder: Search compatible configurations
- ProjectResources resources = getResources(project, false /* isFramework */);
+ ResourceRepository resources = getResources(project, false /* isFramework */);
FolderConfiguration configuration = getConfiguration();
if (configuration != null) { // Not the case when searching from Java files for example
List<ResourceFolder> folders = resources.getFolders(ResourceFolderType.LAYOUT);
@@ -867,34 +980,6 @@ public class Hyperlinks {
return null;
}
- /** Return the resource type of the given url, and the resource name */
- private static Pair<ResourceType,String> parseResource(String url) {
- if (!url.startsWith("@")) { //$NON-NLS-1$
- return null;
- }
- int typeEnd = url.indexOf('/', 1);
- if (typeEnd == -1) {
- return null;
- }
- int nameBegin = typeEnd + 1;
-
- // Skip @ and @+
- int typeBegin = url.startsWith("@+") ? 2 : 1; //$NON-NLS-1$
-
- int colon = url.lastIndexOf(':', typeEnd);
- if (colon != -1) {
- typeBegin = colon + 1;
- }
- String typeName = url.substring(typeBegin, typeEnd);
- ResourceType type = ResourceType.getEnum(typeName);
- if (type == null) {
- return null;
- }
- String name = url.substring(nameBegin);
-
- return Pair.of(type, name);
- }
-
/** Parses the given file and locates a definition of the given resource */
private static Pair<File, Integer> findValueInXml(ResourceType type, String name, File file) {
// We can't use the StructureModelManager on files outside projects
@@ -950,16 +1035,46 @@ public class Hyperlinks {
return null;
}
+ private static IHyperlink[] getStyleLinks(XmlContext context, IRegion range, String url) {
+ Attr attribute = context.getAttribute();
+ if (attribute != null) {
+ // Split up theme resource urls to the nearest dot forwards, such that you
+ // can point to "Theme.Light" by placing the caret anywhere after the dot,
+ // and point to just "Theme" by pointing before it.
+ int caret = context.getInnerRegionCaretOffset();
+ String value = attribute.getValue();
+ int index = value.indexOf('.', caret);
+ if (index != -1) {
+ url = url.substring(0, index);
+ range = new Region(range.getOffset(),
+ range.getLength() - (value.length() - index));
+ }
+ }
+
+ Pair<ResourceType,String> resource = ResourceHelper.parseResource(url);
+ if (resource == null) {
+ String androidStyle = "@android:style/"; //$NON-NLS-1$
+ if (url.startsWith(PREFIX_ANDROID_RESOURCE_REF)) {
+ url = androidStyle + url.substring(PREFIX_ANDROID_RESOURCE_REF.length());
+ } else if (url.startsWith(ANDROID_PKG + ':')) {
+ url = androidStyle + url.substring(ANDROID_PKG.length() + 1);
+ } else {
+ url = "@style/" + url; //$NON-NLS-1$
+ }
+ }
+ return getResourceLinks(range, url);
+ }
+
/**
- * Computes hyperlinks to resource definitions for resource urls (e.g. {@code
- * @android:string/ok} or {@code @layout/foo}. May create multiple links.
+ * Computes hyperlinks to resource definitions for resource urls (e.g.
+ * {@code @android:string/ok} or {@code @layout/foo}. May create multiple links.
*/
private static IHyperlink[] getResourceLinks(IRegion range, String url) {
List<IHyperlink> links = new ArrayList<IHyperlink>();
IProject project = Hyperlinks.getProject();
FolderConfiguration configuration = getConfiguration();
- Pair<ResourceType,String> resource = parseResource(url);
+ Pair<ResourceType,String> resource = ResourceHelper.parseResource(url);
if (resource == null || resource.getFirst() == null) {
return null;
}
@@ -968,7 +1083,7 @@ public class Hyperlinks {
boolean isFramework = url.startsWith("@android"); //$NON-NLS-1$
- ProjectResources resources = getResources(project, isFramework);
+ ResourceRepository resources = getResources(project, isFramework);
if (resources == null) {
return null;
}
@@ -1003,8 +1118,7 @@ public class Hyperlinks {
});
// Is this something found in a values/ folder?
- boolean valueResource = isValueResource(type);
- //boolean fileResource = isFileResource(type);
+ boolean valueResource = ResourceHelper.isValueBasedResourceType(type);
for (ResourceFile file : matches) {
String folderName = file.getFolder().getFolder().getName();
@@ -1066,7 +1180,11 @@ public class Hyperlinks {
range = new Region(range.getOffset() + 1, range.getLength() - 2);
Attr attribute = context.getAttribute();
- if (attribute != null && attribute.getValue().startsWith("@")) { //$NON-NLS-1$
+ if (isStyleAttribute(context)) {
+ return getStyleLinks(context, range, attribute.getValue());
+ }
+ if (attribute != null
+ && attribute.getValue().startsWith(PREFIX_RESOURCE_REF)) {
// Instantly create links for resources since we can use the existing
// resolved maps for this and offer multiple choices for the user
@@ -1082,6 +1200,53 @@ public class Hyperlinks {
if (isElementNameLink(context)) {
isLinkable = true;
}
+ } else if (type == DOMRegionContext.XML_CONTENT) {
+ Node parentNode = context.getNode().getParentNode();
+ if (parentNode != null && parentNode.getNodeType() == Node.ELEMENT_NODE) {
+ // Try to complete resources defined inline as text, such as
+ // style definitions
+ ITextRegion outer = context.getElementRegion();
+ ITextRegion inner = context.getInnerRegion();
+ int innerOffset = outer.getStart() + inner.getStart();
+ int caretOffset = innerOffset + context.getInnerRegionCaretOffset();
+ try {
+ IRegion lineInfo = document.getLineInformationOfOffset(caretOffset);
+ int lineStart = lineInfo.getOffset();
+ int lineEnd = Math.min(lineStart + lineInfo.getLength(),
+ innerOffset + inner.getLength());
+
+ // Compute the resource URL
+ int urlStart = -1;
+ int offset = caretOffset;
+ while (offset > lineStart) {
+ char c = document.getChar(offset);
+ if (c == '@') {
+ urlStart = offset;
+ break;
+ } else if (!isValidResourceUrlChar(c)) {
+ break;
+ }
+ offset--;
+ }
+
+ if (urlStart != -1) {
+ offset = caretOffset;
+ while (offset < lineEnd) {
+ if (!isValidResourceUrlChar(document.getChar(offset))) {
+ break;
+ }
+ offset++;
+ }
+
+ int length = offset - urlStart;
+ String url = document.get(urlStart, length);
+ range = new Region(urlStart, length);
+ return getResourceLinks(range, url);
+ }
+ } catch (BadLocationException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
}
if (isLinkable) {
@@ -1097,6 +1262,11 @@ public class Hyperlinks {
}
}
+ private static boolean isValidResourceUrlChar(char c) {
+ return Character.isJavaIdentifierPart(c) || c == ':' || c == '/' || c == '.' || c == '+';
+
+ }
+
/** Detector for finding Android references in Java files */
public static class JavaResolver extends AbstractHyperlinkDetector {
@@ -1180,7 +1350,7 @@ public class Hyperlinks {
}
/** Returns the editor applicable to this hyperlink detection */
- private static IEditorPart getEditor() {
+ public static IEditorPart getEditor() {
// I would like to be able to find this via getAdapter(TextEditor.class) but
// couldn't find a way to initialize the editor context from
// AndroidSourceViewerConfig#getHyperlinkDetectorTargets (which only has
@@ -1264,7 +1434,7 @@ public class Hyperlinks {
* is known, but the value location within XML files is deferred until the link is
* actually opened.
*/
- private static class ResourceLink implements IHyperlink {
+ static class ResourceLink implements IHyperlink {
private final String mLinkText;
private final IRegion mLinkRegion;
private final ResourceType mType;
@@ -1315,7 +1485,7 @@ public class Hyperlinks {
Pair<IFile,IRegion> def = findIdDefinition(project, mName);
if (def != null) {
try {
- openFile(def.getFirst(), def.getSecond());
+ AdtPlugin.openFile(def.getFirst(), def.getSecond());
} catch (PartInitException e) {
AdtPlugin.log(e, null);
}
@@ -1332,13 +1502,14 @@ public class Hyperlinks {
try {
// Lazily search for the target?
IRegion region = null;
- if (mType != null && mName != null && EXT_XML.equals(file.getFileExtension())) {
+ String extension = file.getFileExtension();
+ if (mType != null && mName != null && EXT_XML.equals(extension)) {
Pair<IFile, IRegion> target = findValueInXml(mType, mName, file);
if (target != null) {
region = target.getSecond();
}
}
- openFile(file, region);
+ AdtPlugin.openFile(file, region);
} catch (PartInitException e) {
AdtPlugin.log(e, null);
}
@@ -1360,6 +1531,10 @@ public class Hyperlinks {
throw new IllegalArgumentException("Invalid link parameters");
}
}
+
+ ResourceFile getFile() {
+ return mFile;
+ }
}
/**
@@ -1367,18 +1542,23 @@ public class Hyperlinks {
* particular caret offset
*/
private static class XmlContext {
+ private final Node mNode;
private final Element mElement;
private final Attr mAttribute;
private final IStructuredDocumentRegion mOuterRegion;
private final ITextRegion mInnerRegion;
+ private final int mInnerRegionOffset;
- public XmlContext(Element element, Attr attribute, IStructuredDocumentRegion outerRegion,
- ITextRegion innerRegion) {
+ public XmlContext(Node node, Element element, Attr attribute,
+ IStructuredDocumentRegion outerRegion,
+ ITextRegion innerRegion, int innerRegionOffset) {
super();
+ mNode = node;
mElement = element;
mAttribute = attribute;
mOuterRegion = outerRegion;
mInnerRegion = innerRegion;
+ mInnerRegionOffset = innerRegionOffset;
}
/**
@@ -1386,6 +1566,16 @@ public class Hyperlinks {
*
* @return the surrounding node
*/
+ public Node getNode() {
+ return mNode;
+ }
+
+
+ /**
+ * Gets the current node, may be null
+ *
+ * @return the surrounding node
+ */
public Element getElement() {
return mElement;
}
@@ -1419,6 +1609,15 @@ public class Hyperlinks {
}
/**
+ * Gets the caret offset relative to the inner region
+ *
+ * @return the offset relative to the inner region
+ */
+ public int getInnerRegionCaretOffset() {
+ return mInnerRegionOffset;
+ }
+
+ /**
* Returns a range with suffix whitespace stripped out
*
* @param document the document containing the regions
@@ -1480,8 +1679,25 @@ public class Hyperlinks {
if (region != null
&& DOMRegionContext.XML_TAG_NAME.equals(region.getType())) {
ITextRegion subRegion = region.getRegionAtCharacterOffset(offset);
- return new XmlContext(element, attribute, region, subRegion);
+ int regionStart = region.getStartOffset();
+ int subregionStart = subRegion.getStart();
+ int relativeOffset = offset - (regionStart + subregionStart);
+ return new XmlContext(element, element, attribute, region, subRegion,
+ relativeOffset);
+ }
+ } else if (inode instanceof Node) {
+ IStructuredDocument doc = model.getStructuredDocument();
+ IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(offset);
+ if (region != null
+ && DOMRegionContext.XML_CONTENT.equals(region.getType())) {
+ ITextRegion subRegion = region.getRegionAtCharacterOffset(offset);
+ int regionStart = region.getStartOffset();
+ int subregionStart = subRegion.getStart();
+ int relativeOffset = offset - (regionStart + subregionStart);
+ return new XmlContext((Node) inode, null, null, region, subRegion,
+ relativeOffset);
}
+
}
}
} finally {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/XmlEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/XmlEditor.java
index 177cb14..4cd2ed5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/XmlEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/XmlEditor.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.editors.xml;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.FirstElementParser;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
@@ -30,6 +30,7 @@ import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PartInitException;
@@ -41,7 +42,7 @@ import org.w3c.dom.Document;
*/
public class XmlEditor extends AndroidXmlEditor {
- public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".xml.XmlEditor"; //$NON-NLS-1$
+ public static final String ID = AdtConstants.EDITORS_NAMESPACE + ".xml.XmlEditor"; //$NON-NLS-1$
/** Root node of the UI element hierarchy */
private UiDocumentNode mUiRootNode;
@@ -73,9 +74,15 @@ public class XmlEditor extends AndroidXmlEditor {
* @return True if the {@link XmlEditor} can handle that file.
*/
public static boolean canHandleFile(IFile file) {
+ if (AdtPlugin.DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, "canHandleFile(%1$s)", file.getFullPath().toOSString());
+ }
// we need the target of the file's project to access the descriptors.
IProject project = file.getProject();
IAndroidTarget target = Sdk.getCurrent().getTarget(project);
+ if (AdtPlugin.DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " target=%1$s", target);
+ }
if (target != null) {
// Note: the target data can be null when an SDK is not finished loading yet.
// We can potentially arrive here when Eclipse is started with a file previously
@@ -85,9 +92,16 @@ public class XmlEditor extends AndroidXmlEditor {
FirstElementParser.Result result = FirstElementParser.parse(
file.getLocation().toOSString(),
SdkConstants.NS_RESOURCES);
+ if (AdtPlugin.DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " data=%1$s, result=%2$s", data, result);
+ }
if (result != null && data != null) {
String name = result.getElement();
+ if (AdtPlugin.DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " name=%1$s, xmlnsprefix=%2$s", name,
+ result.getXmlnsPrefix());
+ }
if (name != null && result.getXmlnsPrefix() != null) {
DocumentDescriptor desc = data.getXmlDescriptors().getDescriptor();
for (ElementDescriptor elem : desc.getChildren()) {
@@ -100,6 +114,10 @@ public class XmlEditor extends AndroidXmlEditor {
}
}
+ if (AdtPlugin.DEBUG_XML_FILE_INIT) {
+ AdtPlugin.log(IStatus.INFO, " File cannot be handled");
+ }
+
return false;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/descriptors/XmlDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/descriptors/XmlDescriptors.java
index b807aa1..bc74d57 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/descriptors/XmlDescriptors.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/descriptors/XmlDescriptors.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.editors.xml.descriptors;
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
import com.android.ide.common.resources.platform.AttributeInfo;
import com.android.ide.common.resources.platform.DeclareStyleableInfo;
@@ -134,7 +134,7 @@ public final class XmlDescriptors implements IDescriptorProvider {
ViewClassInfo[] prefs, ViewClassInfo[] prefGroups) {
XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(
- ANDROID_NS_PREFIX,
+ ANDROID_NS_NAME,
SdkConstants.NS_RESOURCES);
ElementDescriptor searchable = createSearchable(searchableStyleMap, xmlns);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java
index 6ee74ed..4db603e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java
@@ -44,8 +44,8 @@ import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.NullSdkLog;
+import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdklib.xml.ManifestData;
import org.eclipse.core.resources.IFile;
@@ -1187,7 +1187,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// build the command line based on the available parameters.
ArrayList<String> list = new ArrayList<String>();
- list.add(AdtPlugin.getOsAbsoluteEmulator());
+ String path = avdToLaunch.getEmulatorPath(AdtPlugin.getOsSdkFolder());
+
+ list.add(path);
+
list.add(FLAG_AVD);
list.add(avdToLaunch.getName());
@@ -1661,4 +1664,3 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
}
}
-
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java
index a9c8af8..b0dfea3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java
@@ -29,7 +29,7 @@ import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.ddms.DdmsPlugin;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
+import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdkuilib.internal.widgets.AvdSelector;
import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
import com.android.sdkuilib.internal.widgets.AvdSelector.IAvdFilter;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java
index 3705cc2..1fc72fb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java
@@ -26,8 +26,8 @@ import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.NullSdkLog;
+import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.internal.widgets.AvdSelector;
import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java
index cbfcb24..442d356 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.launch;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.sdklib.SdkConstants;
import org.eclipse.core.runtime.CoreException;
@@ -149,7 +149,7 @@ public class JUnitLaunchConfigDelegate extends JUnitLaunchConfigurationDelegate
if (bundle == null) {
throw new IOException("Cannot find org.junit bundle");
}
- URL jarUrl = bundle.getEntry(AndroidConstants.WS_SEP + JUNIT_JAR);
+ URL jarUrl = bundle.getEntry(AdtConstants.WS_SEP + JUNIT_JAR);
return FileLocator.resolve(jarUrl).getFile();
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java
index e747e7e..6364dcd 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java
@@ -18,7 +18,7 @@ package com.android.ide.eclipse.adt.internal.launch;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
@@ -380,7 +380,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
return false;
}
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
String msg = String.format("%1$s is not an Android project!", project.getName());
AdtPlugin.displayError("Android Launch", msg);
return false;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigDelegate.java
index 1f14605..2384f3b 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigDelegate.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigDelegate.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.launch.junit;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.launch.AndroidLaunch;
import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration;
import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController;
@@ -188,7 +188,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
LaunchMessages.AndroidJUnitDelegate_NoRunnerConsoleMsg_4s,
project.getName(),
SdkConstants.CLASS_INSTRUMENTATION_RUNNER,
- AndroidConstants.LIBRARY_TEST_RUNNER,
+ AdtConstants.LIBRARY_TEST_RUNNER,
SdkConstants.FN_ANDROID_MANIFEST_XML));
return null;
} catch (CoreException e) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigurationTab.java
index e5957d7..51db066 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigurationTab.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/AndroidJUnitLaunchConfigurationTab.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.launch.junit;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.launch.LaunchMessages;
import com.android.ide.eclipse.adt.internal.launch.MainLaunchConfigTab;
@@ -659,7 +659,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
validateJavaProject(javaProject);
try {
- if (!project.hasNature(AndroidConstants.NATURE_DEFAULT)) {
+ if (!project.hasNature(AdtConstants.NATURE_DEFAULT)) {
setErrorMessage(
LaunchMessages.NonAndroidProjectError);
return;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/InstrumentationRunnerValidator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/InstrumentationRunnerValidator.java
index bb75bcc..3234b38 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/InstrumentationRunnerValidator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/InstrumentationRunnerValidator.java
@@ -15,7 +15,7 @@
*/
package com.android.ide.eclipse.adt.internal.launch.junit;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.launch.LaunchMessages;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
@@ -88,7 +88,7 @@ class InstrumentationRunnerValidator {
*/
private boolean hasTestRunnerLibrary(ManifestData manifestData) {
for (UsesLibrary lib : manifestData.getUsesLibraries()) {
- if (AndroidConstants.LIBRARY_TEST_RUNNER.equals(lib.getName())) {
+ if (AdtConstants.LIBRARY_TEST_RUNNER.equals(lib.getName())) {
return true;
}
}
@@ -130,7 +130,7 @@ class InstrumentationRunnerValidator {
String validateInstrumentationRunner(String instrumentation) {
if (!mHasRunnerLibrary) {
return String.format(LaunchMessages.InstrValidator_NoTestLibMsg_s,
- AndroidConstants.LIBRARY_TEST_RUNNER);
+ AdtConstants.LIBRARY_TEST_RUNNER);
}
// check if this instrumentation is the standard test runner
if (!instrumentation.equals(SdkConstants.CLASS_INSTRUMENTATION_RUNNER)) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java
index 0f6b508..9ae60c0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java
@@ -18,7 +18,7 @@ package com.android.ide.eclipse.adt.internal.project;
import com.android.ide.common.sdk.LoadStatus;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.AndroidVersion;
@@ -67,6 +67,8 @@ import java.util.regex.Pattern;
*/
public class AndroidClasspathContainerInitializer extends ClasspathContainerInitializer {
+ public static final String NULL_API_URL = "<null>"; //$NON-NLS-1$
+
public static final String SOURCES_ZIP = "/sources.zip"; //$NON-NLS-1$
public static final String COM_ANDROID_IDE_ECLIPSE_ADT_SOURCE =
@@ -293,7 +295,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
}
try {
- BaseProjectHelper.markProject(iProject, AndroidConstants.MARKER_TARGET,
+ BaseProjectHelper.markProject(iProject, AdtConstants.MARKER_TARGET,
markerMessage, IMarker.SEVERITY_ERROR, IMarker.PRIORITY_HIGH);
} catch (CoreException e) {
// In some cases, the workspace may be locked for modification when we
@@ -305,7 +307,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
protected IStatus run(IProgressMonitor monitor) {
try {
BaseProjectHelper.markProject(iProject,
- AndroidConstants.MARKER_TARGET,
+ AdtConstants.MARKER_TARGET,
fmessage, IMarker.SEVERITY_ERROR,
IMarker.PRIORITY_HIGH);
} catch (CoreException e2) {
@@ -324,7 +326,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
// no error, remove potential MARKER_TARGETs.
try {
if (iProject.exists()) {
- iProject.deleteMarkers(AndroidConstants.MARKER_TARGET, true,
+ iProject.deleteMarkers(AdtConstants.MARKER_TARGET, true,
IResource.DEPTH_INFINITE);
}
} catch (CoreException ce) {
@@ -334,7 +336,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
- iProject.deleteMarkers(AndroidConstants.MARKER_TARGET, true,
+ iProject.deleteMarkers(AdtConstants.MARKER_TARGET, true,
IResource.DEPTH_INFINITE);
} catch (CoreException e2) {
return e2.getStatus();
@@ -530,8 +532,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
}
}
IClasspathAttribute[] attributes = null;
- if (apiURL != null) {
-
+ if (apiURL != null && !NULL_API_URL.equals(apiURL)) {
IClasspathAttribute cpAttribute = JavaCore.newClasspathAttribute(
IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, apiURL);
attributes = new IClasspathAttribute[] {
@@ -824,12 +825,22 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
getAndroidSourceProperty(target), null);
}
IClasspathAttribute[] extraAttributtes = entry.getExtraAttributes();
+ if (extraAttributtes.length == 0) {
+ ProjectHelper.saveStringProperty(root, PROPERTY_ANDROID_API,
+ NULL_API_URL);
+ }
for (int j = 0; j < extraAttributtes.length; j++) {
IClasspathAttribute extraAttribute = extraAttributtes[j];
+ String value = extraAttribute.getValue();
+ if ((value == null || value.trim().length() == 0)
+ && IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME
+ .equals(extraAttribute.getName())) {
+ value = NULL_API_URL;
+ }
if (IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME
.equals(extraAttribute.getName())) {
ProjectHelper.saveStringProperty(root,
- PROPERTY_ANDROID_API, extraAttribute.getValue());
+ PROPERTY_ANDROID_API, value);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestHelper.java
index cafd268..ba47c7a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestHelper.java
@@ -19,9 +19,9 @@ package com.android.ide.eclipse.adt.internal.project;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.XmlErrorListener;
import com.android.ide.eclipse.adt.io.IFileWrapper;
-import com.android.sdklib.io.FileWrapper;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.StreamException;
+import com.android.io.FileWrapper;
+import com.android.io.IAbstractFile;
+import com.android.io.StreamException;
import com.android.sdklib.xml.AndroidManifestParser;
import com.android.sdklib.xml.ManifestData;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidNature.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidNature.java
index 5e05c99..dddc7a0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidNature.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidNature.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.project;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.build.builders.PostCompilerBuilder;
import com.android.ide.eclipse.adt.internal.build.builders.PreCompilerBuilder;
import com.android.ide.eclipse.adt.internal.build.builders.ResourceManagerBuilder;
@@ -127,7 +127,7 @@ public class AndroidNature implements IProjectNature {
// Adding the java nature after the android one, would place the java builder before the
// android builders.
addNatureToProjectDescription(project, JavaCore.NATURE_ID, monitor);
- addNatureToProjectDescription(project, AndroidConstants.NATURE_DEFAULT, monitor);
+ addNatureToProjectDescription(project, AdtConstants.NATURE_DEFAULT, monitor);
}
/**
@@ -151,7 +151,7 @@ public class AndroidNature implements IProjectNature {
String[] newNatures = new String[natures.length + 1];
// Android natures always come first.
- if (natureId.equals(AndroidConstants.NATURE_DEFAULT)) {
+ if (natureId.equals(AdtConstants.NATURE_DEFAULT)) {
System.arraycopy(natures, 0, newNatures, 1, natures.length);
newNatures[0] = natureId;
} else {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java
index 6485f96..8c4d08d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.project;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
@@ -110,6 +110,26 @@ public final class BaseProjectHelper {
*/
public final static IMarker markResource(IResource resource, String markerId,
String message, int lineNumber, int severity) {
+ return markResource(resource, markerId, message, lineNumber, -1, -1, severity);
+ }
+
+ /**
+ * Adds a marker to a file on a specific line, for a specific range of text. This
+ * methods catches thrown {@link CoreException}, and returns null instead.
+ *
+ * @param resource the resource to be marked
+ * @param markerId The id of the marker to add.
+ * @param message the message associated with the mark
+ * @param lineNumber the line number where to put the mark. If line is < 1, it puts
+ * the marker on line 1,
+ * @param startOffset the beginning offset of the marker (relative to the beginning of
+ * the document, not the line), or -1 for no range
+ * @param endOffset the ending offset of the marker
+ * @param severity the severity of the marker.
+ * @return the IMarker that was added or null if it failed to add one.
+ */
+ public final static IMarker markResource(IResource resource, String markerId,
+ String message, int lineNumber, int startOffset, int endOffset, int severity) {
try {
IMarker marker = resource.createMarker(markerId);
marker.setAttribute(IMarker.MESSAGE, message);
@@ -125,6 +145,11 @@ public final class BaseProjectHelper {
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
}
+ if (startOffset != -1) {
+ marker.setAttribute(IMarker.CHAR_START, startOffset);
+ marker.setAttribute(IMarker.CHAR_END, endOffset);
+ }
+
// on Windows, when adding a marker to a project, it takes a refresh for the marker
// to show. In order to fix this we're forcing a refresh of elements receiving
// markers (and only the element, not its children), to force the marker display.
@@ -402,7 +427,7 @@ public final class BaseProjectHelper {
// check if it's an android project based on its nature
try {
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT)) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT)) {
if (filter == null || filter.accept(project)) {
androidProjectList.add(javaProject);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
index 3b0622a..3d0e088 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.project;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AndroidPrintStream;
import com.android.ide.eclipse.adt.internal.build.BuildHelper;
import com.android.ide.eclipse.adt.internal.build.DexException;
@@ -124,7 +124,7 @@ public final class ExportHelper {
// tmp file for the packaged resource file. To not disturb the incremental builders
// output, all intermediary files are created in tmp files.
- File resourceFile = File.createTempFile(TEMP_PREFIX, AndroidConstants.DOT_RES);
+ File resourceFile = File.createTempFile(TEMP_PREFIX, AdtConstants.DOT_RES);
resourceFile.deleteOnExit();
// package the resources.
@@ -139,7 +139,7 @@ public final class ExportHelper {
// Step 2. Convert the byte code to Dalvik bytecode
// tmp file for the packaged resource file.
- File dexFile = File.createTempFile(TEMP_PREFIX, AndroidConstants.DOT_DEX);
+ File dexFile = File.createTempFile(TEMP_PREFIX, AdtConstants.DOT_DEX);
dexFile.deleteOnExit();
ProjectState state = Sdk.getProjectState(project);
@@ -163,7 +163,7 @@ public final class ExportHelper {
String[] projectOutputs = helper.getProjectOutputs();
// create a jar from the output of these projects
- File inputJar = File.createTempFile(TEMP_PREFIX, AndroidConstants.DOT_JAR);
+ File inputJar = File.createTempFile(TEMP_PREFIX, AdtConstants.DOT_JAR);
inputJar.deleteOnExit();
JarOutputStream jos = new JarOutputStream(new FileOutputStream(inputJar));
@@ -180,7 +180,7 @@ public final class ExportHelper {
null /*resourceMarker*/);
// destination file for proguard
- File obfuscatedJar = File.createTempFile(TEMP_PREFIX, AndroidConstants.DOT_JAR);
+ File obfuscatedJar = File.createTempFile(TEMP_PREFIX, AdtConstants.DOT_JAR);
obfuscatedJar.deleteOnExit();
// run proguard
@@ -274,7 +274,7 @@ public final class ExportHelper {
IPath binLocation = outputFolder.getLocation();
// make the full path to the package
- String fileName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
+ String fileName = project.getName() + AdtConstants.DOT_ANDROID_PACKAGE;
File file = new File(binLocation.toOSString() + File.separator + fileName);
@@ -348,7 +348,7 @@ public final class ExportHelper {
} else if (file.isFile()) {
// check the extension
String name = file.getName();
- if (name.toLowerCase().endsWith(AndroidConstants.DOT_CLASS) == false) {
+ if (name.toLowerCase().endsWith(AdtConstants.DOT_CLASS) == false) {
return;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java
index 7ca244c..394338b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.project;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.SdkConstants;
@@ -54,7 +54,7 @@ public class FolderDecorator implements ILightweightLabelDecorator {
}
try {
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT)) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT)) {
// check the folder is directly under the project.
if (folder.getParent().getType() == IResource.PROJECT) {
String name = folder.getName();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
index 91e2380..ca988a2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.project;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.xml.ManifestData;
@@ -116,11 +116,11 @@ public final class ProjectHelper {
public static String getJavaDocPath(String javaDocOSLocation) {
// first thing we do is convert the \ into /
String javaDoc = javaDocOSLocation.replaceAll("\\\\", //$NON-NLS-1$
- AndroidConstants.WS_SEP);
+ AdtConstants.WS_SEP);
// then we add file: at the beginning for unix path, and file:/ for non
// unix path
- if (javaDoc.startsWith(AndroidConstants.WS_SEP)) {
+ if (javaDoc.startsWith(AdtConstants.WS_SEP)) {
return "file:" + javaDoc; //$NON-NLS-1$
}
@@ -365,11 +365,11 @@ public final class ProjectHelper {
if (checkCompilerCompliance(javaProject) != COMPILER_COMPLIANCE_OK) {
// setup the preferred compiler compliance level.
javaProject.setOption(JavaCore.COMPILER_COMPLIANCE,
- AndroidConstants.COMPILER_COMPLIANCE_PREFERRED);
+ AdtConstants.COMPILER_COMPLIANCE_PREFERRED);
javaProject.setOption(JavaCore.COMPILER_SOURCE,
- AndroidConstants.COMPILER_COMPLIANCE_PREFERRED);
+ AdtConstants.COMPILER_COMPLIANCE_PREFERRED);
javaProject.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
- AndroidConstants.COMPILER_COMPLIANCE_PREFERRED);
+ AdtConstants.COMPILER_COMPLIANCE_PREFERRED);
// clean the project to make sure we recompile
try {
@@ -402,7 +402,7 @@ public final class ProjectHelper {
for (IProject p : projects) {
if (p.isOpen()) {
try {
- if (p.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (p.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
// ignore non android projects
continue;
}
@@ -451,16 +451,16 @@ public final class ProjectHelper {
String[] natures = description.getNatureIds();
// if the android nature is not the first one, we reorder them
- if (AndroidConstants.NATURE_DEFAULT.equals(natures[0]) == false) {
+ if (AdtConstants.NATURE_DEFAULT.equals(natures[0]) == false) {
// look for the index
for (int i = 0 ; i < natures.length ; i++) {
- if (AndroidConstants.NATURE_DEFAULT.equals(natures[i])) {
+ if (AdtConstants.NATURE_DEFAULT.equals(natures[i])) {
// if we try to just reorder the array in one pass, this doesn't do
// anything. I guess JDT check that we are actually adding/removing nature.
// So, first we'll remove the android nature, and then add it back.
// remove the android nature
- removeNature(project, AndroidConstants.NATURE_DEFAULT);
+ removeNature(project, AdtConstants.NATURE_DEFAULT);
// now add it back at the first index.
description = project.getDescription();
@@ -469,7 +469,7 @@ public final class ProjectHelper {
String[] newNatures = new String[natures.length + 1];
// first one is android
- newNatures[0] = AndroidConstants.NATURE_DEFAULT;
+ newNatures[0] = AdtConstants.NATURE_DEFAULT;
// next the rest that was before the android nature
System.arraycopy(natures, 0, newNatures, 1, natures.length);
@@ -675,7 +675,7 @@ public final class ProjectHelper {
* @return true if the option value is supproted.
*/
private static boolean checkCompliance(String optionValue) {
- for (String s : AndroidConstants.COMPILER_COMPLIANCE) {
+ for (String s : AdtConstants.COMPILER_COMPLIANCE) {
if (s != null && s.equals(optionValue)) {
return true;
}
@@ -691,10 +691,10 @@ public final class ProjectHelper {
*/
public static String getApkFilename(IProject project, String config) {
if (config != null) {
- return project.getName() + "-" + config + AndroidConstants.DOT_ANDROID_PACKAGE; //$NON-NLS-1$
+ return project.getName() + "-" + config + AdtConstants.DOT_ANDROID_PACKAGE; //$NON-NLS-1$
}
- return project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
+ return project.getName() + AdtConstants.DOT_ANDROID_PACKAGE;
}
/**
@@ -718,7 +718,7 @@ public final class ProjectHelper {
//Verify that the project has also the Android Nature
try {
- if (!androidJavaProject.getProject().hasNature(AndroidConstants.NATURE_DEFAULT)) {
+ if (!androidJavaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT)) {
continue;
}
} catch (CoreException e) {
@@ -750,7 +750,7 @@ public final class ProjectHelper {
// get the package path
- String packageName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
+ String packageName = project.getName() + AdtConstants.DOT_ANDROID_PACKAGE;
IResource r = outputLocation.findMember(packageName);
// check the package is present
@@ -772,7 +772,7 @@ public final class ProjectHelper {
* is missing.
*/
public static IFile getManifest(IProject project) {
- IResource r = project.findMember(AndroidConstants.WS_SEP
+ IResource r = project.findMember(AdtConstants.WS_SEP
+ SdkConstants.FN_ANDROID_MANIFEST_XML);
if (r == null || r.exists() == false || (r instanceof IFile) == false) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/XmlErrorHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/XmlErrorHandler.java
index d5f6f15..7cd0161 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/XmlErrorHandler.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/XmlErrorHandler.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.project;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.sdklib.xml.AndroidManifestParser.ManifestErrorHandler;
import org.eclipse.core.resources.IFile;
@@ -97,7 +97,7 @@ public class XmlErrorHandler extends DefaultHandler implements ManifestErrorHand
public void warning(SAXParseException exception) throws SAXException {
if (mFile != null) {
BaseProjectHelper.markResource(mFile,
- AndroidConstants.MARKER_XML,
+ AdtConstants.MARKER_XML,
exception.getMessage(),
exception.getLineNumber(),
IMarker.SEVERITY_WARNING);
@@ -125,7 +125,7 @@ public class XmlErrorHandler extends DefaultHandler implements ManifestErrorHand
if (mFile != null) {
BaseProjectHelper.markResource(mFile,
- AndroidConstants.MARKER_XML,
+ AdtConstants.MARKER_XML,
message,
lineNumber,
IMarker.SEVERITY_ERROR);
@@ -156,14 +156,14 @@ public class XmlErrorHandler extends DefaultHandler implements ManifestErrorHand
// mark the file
IMarker marker = BaseProjectHelper.markResource(getFile(),
- AndroidConstants.MARKER_ANDROID, result, line, IMarker.SEVERITY_ERROR);
+ AdtConstants.MARKER_ANDROID, result, line, IMarker.SEVERITY_ERROR);
// add custom attributes to be used by the manifest editor.
if (marker != null) {
try {
- marker.setAttribute(AndroidConstants.MARKER_ATTR_TYPE,
- AndroidConstants.MARKER_ATTR_TYPE_ACTIVITY);
- marker.setAttribute(AndroidConstants.MARKER_ATTR_CLASS, className);
+ marker.setAttribute(AdtConstants.MARKER_ATTR_TYPE,
+ AdtConstants.MARKER_ATTR_TYPE_ACTIVITY);
+ marker.setAttribute(AdtConstants.MARKER_ATTR_CLASS, className);
} catch (CoreException e) {
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidDocumentChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidDocumentChange.java
index f6677f8..fe3c014 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidDocumentChange.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidDocumentChange.java
@@ -277,7 +277,7 @@ public class AndroidDocumentChange extends DocumentChange {
if (name != null) {
String newValue;
if (combinePackage) {
- newValue = RefactoringUtil.getNewValue(getAppPackage(), name, newName);
+ newValue = AndroidManifest.extractActivityName(newName, getAppPackage());
} else {
newValue = newName;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidPackageRenameParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidPackageRenameParticipant.java
index 43cb09d..3a8348d 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidPackageRenameParticipant.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidPackageRenameParticipant.java
@@ -16,8 +16,9 @@
package com.android.ide.eclipse.adt.internal.refactoring.core;
+import com.android.AndroidConstants;
import com.android.ide.common.layout.LayoutConstants;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChange;
import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChangeDescription;
@@ -203,7 +204,7 @@ public class AndroidPackageRenameParticipant extends AndroidRenameParticipant {
IJavaProject javaProject = (IJavaProject) mPackageFragment
.getAncestor(IJavaElement.JAVA_PROJECT);
IProject project = javaProject.getProject();
- IResource manifestResource = project.findMember(AndroidConstants.WS_SEP
+ IResource manifestResource = project.findMember(AdtConstants.WS_SEP
+ SdkConstants.FN_ANDROID_MANIFEST_XML);
if (manifestResource == null || !manifestResource.exists()
@@ -297,7 +298,7 @@ public class AndroidPackageRenameParticipant extends AndroidRenameParticipant {
IResource resource = layoutMembers[j];
if (resource instanceof IFolder
&& resource.exists()
- && resource.getName().startsWith(SdkConstants.FD_LAYOUT)) {
+ && resource.getName().startsWith(AndroidConstants.FD_RES_LAYOUT)) {
IFolder layoutFolder = (IFolder) resource;
IResource[] members = layoutFolder.members();
for (int i = 0; i < members.length; i++) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeMoveParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeMoveParticipant.java
index e7c3e1a..735c595 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeMoveParticipant.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeMoveParticipant.java
@@ -16,8 +16,9 @@
package com.android.ide.eclipse.adt.internal.refactoring.core;
+import com.android.AndroidConstants;
import com.android.ide.common.layout.LayoutConstants;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChange;
import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChangeDescription;
@@ -174,7 +175,7 @@ public class AndroidTypeMoveParticipant extends MoveParticipant {
IType type = (IType) element;
IJavaProject javaProject = (IJavaProject) type.getAncestor(IJavaElement.JAVA_PROJECT);
IProject project = javaProject.getProject();
- IResource manifestResource = project.findMember(AndroidConstants.WS_SEP
+ IResource manifestResource = project.findMember(AdtConstants.WS_SEP
+ SdkConstants.FN_ANDROID_MANIFEST_XML);
if (manifestResource == null || !manifestResource.exists()
@@ -232,7 +233,7 @@ public class AndroidTypeMoveParticipant extends MoveParticipant {
private void addLayoutChanges(IProject project, String className) {
try {
IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES);
- IFolder layoutFolder = resFolder.getFolder(SdkConstants.FD_LAYOUT);
+ IFolder layoutFolder = resFolder.getFolder(AndroidConstants.FD_RES_LAYOUT);
IResource[] members = layoutFolder.members();
for (int i = 0; i < members.length; i++) {
IResource member = members[i];
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeRenameParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeRenameParticipant.java
index dc393e3..b80125a 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeRenameParticipant.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeRenameParticipant.java
@@ -16,8 +16,9 @@
package com.android.ide.eclipse.adt.internal.refactoring.core;
+import com.android.AndroidConstants;
import com.android.ide.common.layout.LayoutConstants;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChange;
import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChangeDescription;
@@ -129,7 +130,7 @@ public class AndroidTypeRenameParticipant extends AndroidRenameParticipant {
IType type = (IType) element;
IJavaProject javaProject = (IJavaProject) type.getAncestor(IJavaElement.JAVA_PROJECT);
IProject project = javaProject.getProject();
- IResource manifestResource = project.findMember(AndroidConstants.WS_SEP
+ IResource manifestResource = project.findMember(AdtConstants.WS_SEP
+ SdkConstants.FN_ANDROID_MANIFEST_XML);
if (manifestResource == null || !manifestResource.exists()
@@ -193,7 +194,7 @@ public class AndroidTypeRenameParticipant extends AndroidRenameParticipant {
private void addLayoutChanges(IProject project, String className) {
try {
IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES);
- IFolder layoutFolder = resFolder.getFolder(SdkConstants.FD_LAYOUT);
+ IFolder layoutFolder = resFolder.getFolder(AndroidConstants.FD_RES_LAYOUT);
IResource[] members = layoutFolder.members();
for (int i = 0; i < members.length; i++) {
IResource member = members[i];
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/RefactoringUtil.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/RefactoringUtil.java
index 409cf72..adc4d5a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/RefactoringUtil.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/RefactoringUtil.java
@@ -41,49 +41,6 @@ public class RefactoringUtil {
private static boolean sRefactorAppPackage = false;
/**
- * Returns the new class name combined with a package name
- * the oldName and newName are class names as found in the manifest
- * (for instance with a leading dot or with a single element,
- * that needs to be recombined with a package name)
- *
- * @param javaPackage the package name
- * @param oldName the old name
- * @param newName the new name
- *
- * @return the new name
- */
- public static String getNewValue(String javaPackage, String oldName, String newName) {
- if (oldName == null || oldName.length() == 0) {
- return null;
- }
- if (javaPackage == null || javaPackage.length() == 0) {
- return null;
- }
- if (newName == null || newName.length() == 0) {
- return null;
- }
- if (!newName.startsWith(javaPackage + ".")) { //$NON-NLS-1$
- return newName;
- } else if (newName.length() > (javaPackage.length() + 1)) {
- String value = newName.substring(javaPackage.length() + 1);
- return value;
- }
- boolean startWithDot = (oldName.charAt(0) == '.');
- boolean hasDot = (oldName.indexOf('.') != -1);
- if (startWithDot || !hasDot) {
-
- if (startWithDot) {
- return "." + newName;
- } else {
- int lastPeriod = newName.lastIndexOf(".");
- return newName.substring(lastPeriod + 1);
- }
- } else {
- return newName;
- }
- }
-
- /**
* Releases SSE read model; saves SSE model if exists edit model
* Called in dispose method of refactoring change classes
*
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringAction.java
index ffa4089..e005f1c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringAction.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.refactorings.extractstring;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -165,7 +165,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
if (file.exists()) {
IProject proj = file.getProject();
try {
- if (proj != null && proj.hasNature(AndroidConstants.NATURE_DEFAULT)) {
+ if (proj != null && proj.hasNature(AdtConstants.NATURE_DEFAULT)) {
return file;
}
} catch (CoreException e) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java
index 8dab07e..dee8407 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java
@@ -17,11 +17,12 @@
package com.android.ide.eclipse.adt.internal.refactorings.extractstring;
-import com.android.ide.eclipse.adt.AndroidConstants;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
+import com.android.AndroidConstants;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.SelectorMode;
+import com.android.resources.ResourceFolderType;
import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFolder;
@@ -83,10 +84,10 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
"/res/[a-z][a-zA-Z0-9_-]+/[^.]+\\.xml"); //$NON-NLS-1$
/** Absolute destination folder root, e.g. "/res/" */
private static final String RES_FOLDER_ABS =
- AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
+ AdtConstants.WS_RESOURCES + AdtConstants.WS_SEP;
/** Relative destination folder root, e.g. "res/" */
private static final String RES_FOLDER_REL =
- SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
+ SdkConstants.FD_RESOURCES + AdtConstants.WS_SEP;
private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml"; //$NON-NLS-1$
@@ -470,7 +471,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
mConfigSelector.getConfiguration(mTempConfig);
StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES));
- sb.append(AndroidConstants.WS_SEP);
+ sb.append(AdtConstants.WS_SEP);
String newPath = sb.toString();
@@ -571,12 +572,12 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
- int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR);
+ int pos = wsFolderPath.indexOf(AdtConstants.WS_SEP_CHAR);
if (pos >= 0) {
wsFolderPath = wsFolderPath.substring(0, pos);
}
- String[] folderSegments = wsFolderPath.split(FolderConfiguration.QUALIFIER_SEP);
+ String[] folderSegments = wsFolderPath.split(AndroidConstants.RES_QUALIFIER_SEP);
if (folderSegments.length > 0) {
String folderName = folderSegments[0];
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringRefactoring.java
index 1a41caf..955a0b2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringRefactoring.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringRefactoring.java
@@ -18,7 +18,7 @@ package com.android.ide.eclipse.adt.internal.refactorings.extractstring;
import static com.android.ide.common.layout.LayoutConstants.STRING_PREFIX;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ReferenceAttributeDescriptor;
@@ -26,7 +26,7 @@ import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.Resour
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
+import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.xml.ManifestData;
@@ -424,7 +424,7 @@ public class ExtractStringRefactoring extends Refactoring {
}
// Check this a Layout XML file and get the selection and its context.
- if (mFile != null && AndroidConstants.EXT_XML.equals(mFile.getFileExtension())) {
+ if (mFile != null && AdtConstants.EXT_XML.equals(mFile.getFileExtension())) {
// Currently we only support Android resource XML files, so they must have a path
// similar to
@@ -1016,7 +1016,7 @@ public class ExtractStringRefactoring extends Refactoring {
// Add all /res folders (technically we don't need to process /res/values
// XML files that contain resources/string elements, but it's easier to
// not filter them out.)
- IFolder f = mProject.getFolder(AndroidConstants.WS_RESOURCES);
+ IFolder f = mProject.getFolder(AdtConstants.WS_RESOURCES);
if (f.exists()) {
try {
mFolders.addAll(
@@ -1055,7 +1055,7 @@ public class ExtractStringRefactoring extends Refactoring {
if (res.exists() && !res.isDerived() && res instanceof IFile) {
IFile file = (IFile) res;
// Must have an XML extension
- if (AndroidConstants.EXT_XML.equals(file.getFileExtension())) {
+ if (AdtConstants.EXT_XML.equals(file.getFileExtension())) {
IPath p = file.getFullPath();
// And not be either paths we want to filter out
if ((mFilterPath1 != null && mFilterPath1.equals(p)) ||
@@ -1101,7 +1101,7 @@ public class ExtractStringRefactoring extends Refactoring {
SubMonitor monitor) {
TextFileChange xmlChange = new TextFileChange(getName(), targetXml);
- xmlChange.setTextType(AndroidConstants.EXT_XML);
+ xmlChange.setTextType(AdtConstants.EXT_XML);
String error = ""; //$NON-NLS-1$
TextEdit edit = null;
@@ -1478,7 +1478,7 @@ public class ExtractStringRefactoring extends Refactoring {
HashSet<IFile> files = new HashSet<IFile>();
files.add(sourceFile);
- if (allConfigurations && AndroidConstants.EXT_XML.equals(sourceFile.getFileExtension())) {
+ if (allConfigurations && AdtConstants.EXT_XML.equals(sourceFile.getFileExtension())) {
IPath path = sourceFile.getFullPath();
if (path.segmentCount() == 4 && path.segment(1).equals(SdkConstants.FD_RESOURCES)) {
IProject project = sourceFile.getProject();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/renamepackage/ApplicationPackageNameRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/renamepackage/ApplicationPackageNameRefactoring.java
index 9097f97..3df35bc 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/renamepackage/ApplicationPackageNameRefactoring.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/renamepackage/ApplicationPackageNameRefactoring.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.refactorings.renamepackage;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.xml.AndroidManifest;
@@ -153,7 +153,7 @@ class ApplicationPackageNameRefactoring extends Refactoring {
ImportRewrite irw = ImportRewrite.create(cu, true);
irw.addImport(mNewPackageName.getFullyQualifiedName() + '.'
- + AndroidConstants.FN_RESOURCE_BASE);
+ + AdtConstants.FN_RESOURCE_BASE);
try {
rewrittenImports.addChild( irw.rewriteImports(null) );
@@ -210,14 +210,14 @@ class ApplicationPackageNameRefactoring extends Refactoring {
}
TextFileChange xmlChange = new TextFileChange("XML resource file edit", file);
- xmlChange.setTextType(AndroidConstants.EXT_XML);
+ xmlChange.setTextType(AdtConstants.EXT_XML);
MultiTextEdit multiEdit = new MultiTextEdit();
ArrayList<TextEditGroup> editGroups = new ArrayList<TextEditGroup>();
- final String oldAppNamespaceString = String.format(AndroidConstants.NS_CUSTOM_RESOURCES,
+ final String oldAppNamespaceString = String.format(AdtConstants.NS_CUSTOM_RESOURCES,
mOldPackageName.getFullyQualifiedName());
- final String newAppNamespaceString = String.format(AndroidConstants.NS_CUSTOM_RESOURCES,
+ final String newAppNamespaceString = String.format(AdtConstants.NS_CUSTOM_RESOURCES,
mNewPackageName.getFullyQualifiedName());
// Prepare the change set
@@ -301,7 +301,7 @@ class ApplicationPackageNameRefactoring extends Refactoring {
}
TextFileChange xmlChange = new TextFileChange("Make Manifest edits", file);
- xmlChange.setTextType(AndroidConstants.EXT_XML);
+ xmlChange.setTextType(AdtConstants.EXT_XML);
MultiTextEdit multiEdit = new MultiTextEdit();
ArrayList<TextEditGroup> editGroups = new ArrayList<TextEditGroup>();
@@ -417,7 +417,7 @@ class ApplicationPackageNameRefactoring extends Refactoring {
public boolean visit(IResource resource) throws CoreException {
if (resource instanceof IFile) {
IFile file = (IFile) resource;
- if (AndroidConstants.EXT_JAVA.equals(file.getFileExtension())) {
+ if (AdtConstants.EXT_JAVA.equals(file.getFileExtension())) {
ICompilationUnit icu = JavaCore.createCompilationUnitFrom(file);
@@ -430,7 +430,7 @@ class ApplicationPackageNameRefactoring extends Refactoring {
edit.addChild(text_edit);
TextFileChange text_file_change = new TextFileChange(file.getName(), file);
- text_file_change.setTextType(AndroidConstants.EXT_JAVA);
+ text_file_change.setTextType(AdtConstants.EXT_JAVA);
text_file_change.setEdit(edit);
mChanges.add(text_file_change);
}
@@ -438,7 +438,7 @@ class ApplicationPackageNameRefactoring extends Refactoring {
// XXX Partially taken from ExtractStringRefactoring.java
// Check this a Layout XML file and get the selection and
// its context.
- } else if (AndroidConstants.EXT_XML.equals(file.getFileExtension())) {
+ } else if (AdtConstants.EXT_XML.equals(file.getFileExtension())) {
if (SdkConstants.FN_ANDROID_MANIFEST_XML.equals(file.getName())) {
@@ -510,7 +510,7 @@ class ApplicationPackageNameRefactoring extends Refactoring {
QualifiedName qualifiedImportName = (QualifiedName) importName;
if (qualifiedImportName.getName().getIdentifier()
- .equals(AndroidConstants.FN_RESOURCE_BASE)) {
+ .equals(AdtConstants.FN_RESOURCE_BASE)) {
mRewriter.replace(qualifiedImportName.getQualifier(), mNewPackageName,
null);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/CyclicDependencyValidator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/CyclicDependencyValidator.java
new file mode 100644
index 0000000..625adc6
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/CyclicDependencyValidator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 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;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.dialogs.IInputValidator;
+
+import java.util.Collection;
+
+/** A validator which checks for cyclic dependencies */
+public class CyclicDependencyValidator implements IInputValidator {
+ private final Collection<String> mInvalidIds;
+
+ private CyclicDependencyValidator(Collection<String> invalid) {
+ this.mInvalidIds = invalid;
+ }
+
+ public String isValid(String newText) {
+ if (mInvalidIds.contains(newText)) {
+ return String.format(
+ "Cyclic include, not valid",
+ newText);
+ }
+ return null;
+ }
+
+ /**
+ * Creates a validator which ensures that the chosen id is not for a layout that is
+ * directly or indirectly including the given layout. Used to avoid cyclic
+ * dependencies when offering layouts to be included within a given file, etc.
+ *
+ * @param file the target file that candidate layouts should not directly or
+ * indirectly include
+ * @return a validator which checks whether resource ids are valid or whether they
+ * could result in a cyclic dependency
+ */
+ public static IInputValidator create(IFile file) {
+ IProject project = file.getProject();
+ IncludeFinder includeFinder = IncludeFinder.get(project);
+ final Collection<String> invalid =
+ includeFinder.getInvalidIncludes(file);
+
+ return new CyclicDependencyValidator(invalid);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IResourceRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IResourceRepository.java
deleted file mode 100644
index 1abd9eb..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IResourceRepository.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.internal.resources;
-
-import com.android.resources.ResourceType;
-
-/**
- * A repository of resources. This allows access to the resource by {@link ResourceType}.
- */
-public interface IResourceRepository {
-
- /**
- * Returns the present {@link ResourceType}s in the project.
- * @return an array containing all the type of resources existing in the project.
- */
- public abstract ResourceType[] getAvailableResourceTypes();
-
- /**
- * Returns an array of the existing resource for the specified type.
- * @param type the type of the resources to return
- */
- public abstract ResourceItem[] getResources(ResourceType type);
-
- /**
- * Returns whether resources of the specified type are present.
- * @param type the type of the resources to check.
- */
- public abstract boolean hasResources(ResourceType type);
-
- /**
- * Returns whether the repository is a system repository.
- */
- public abstract boolean isSystemRepository();
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java
index 327bd89..467ae49 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java
@@ -16,27 +16,431 @@
package com.android.ide.eclipse.adt.internal.resources;
+import static com.android.AndroidConstants.FD_RES_VALUES;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_STYLE;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_STYLE;
+import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.NAME_ATTR;
+import static com.android.sdklib.SdkConstants.FD_RESOURCES;
+
+import com.android.ide.common.resources.ResourceDeltaKind;
+import com.android.ide.common.resources.configuration.CountryCodeQualifier;
+import com.android.ide.common.resources.configuration.DockModeQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
+import com.android.ide.common.resources.configuration.LanguageQualifier;
+import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
+import com.android.ide.common.resources.configuration.NavigationStateQualifier;
+import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
+import com.android.ide.common.resources.configuration.NightModeQualifier;
+import com.android.ide.common.resources.configuration.PixelDensityQualifier;
+import com.android.ide.common.resources.configuration.RegionQualifier;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
+import com.android.ide.common.resources.configuration.TouchScreenQualifier;
+import com.android.ide.common.resources.configuration.VersionQualifier;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.VisualRefactoring;
+import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
+import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+/**
+ * Helper class to deal with SWT specifics for the resources.
+ */
+@SuppressWarnings("restriction") // XML model
public class ResourceHelper {
+ private final static Map<Class<?>, Image> sIconMap = new HashMap<Class<?>, Image>(
+ FolderConfiguration.getQualifierCount());
+
+ static {
+ IconFactory factory = IconFactory.getInstance();
+ sIconMap.put(CountryCodeQualifier.class, factory.getIcon("mcc")); //$NON-NLS-1$
+ sIconMap.put(NetworkCodeQualifier.class, factory.getIcon("mnc")); //$NON-NLS-1$
+ sIconMap.put(LanguageQualifier.class, factory.getIcon("language")); //$NON-NLS-1$
+ sIconMap.put(RegionQualifier.class, factory.getIcon("region")); //$NON-NLS-1$
+ sIconMap.put(ScreenSizeQualifier.class, factory.getIcon("size")); //$NON-NLS-1$
+ sIconMap.put(ScreenRatioQualifier.class, factory.getIcon("ratio")); //$NON-NLS-1$
+ sIconMap.put(ScreenOrientationQualifier.class, factory.getIcon("orientation")); //$NON-NLS-1$
+ sIconMap.put(DockModeQualifier.class, factory.getIcon("dockmode")); //$NON-NLS-1$
+ sIconMap.put(NightModeQualifier.class, factory.getIcon("nightmode")); //$NON-NLS-1$
+ sIconMap.put(PixelDensityQualifier.class, factory.getIcon("dpi")); //$NON-NLS-1$
+ sIconMap.put(TouchScreenQualifier.class, factory.getIcon("touch")); //$NON-NLS-1$
+ sIconMap.put(KeyboardStateQualifier.class, factory.getIcon("keyboard")); //$NON-NLS-1$
+ sIconMap.put(TextInputMethodQualifier.class, factory.getIcon("text_input")); //$NON-NLS-1$
+ sIconMap.put(NavigationStateQualifier.class, factory.getIcon("navpad")); //$NON-NLS-1$
+ sIconMap.put(NavigationMethodQualifier.class, factory.getIcon("navpad")); //$NON-NLS-1$
+ sIconMap.put(ScreenDimensionQualifier.class, factory.getIcon("dimension")); //$NON-NLS-1$
+ sIconMap.put(VersionQualifier.class, factory.getIcon("version")); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the icon for the qualifier.
+ */
+ public static Image getIcon(Class<? extends ResourceQualifier> theClass) {
+ return sIconMap.get(theClass);
+ }
+
+ /**
+ * Returns a {@link ResourceDeltaKind} from an {@link IResourceDelta} value.
+ * @param kind a {@link IResourceDelta} integer constant.
+ * @return a matching {@link ResourceDeltaKind} or null.
+ *
+ * @see IResourceDelta#ADDED
+ * @see IResourceDelta#REMOVED
+ * @see IResourceDelta#CHANGED
+ */
+ public static ResourceDeltaKind getResourceDeltaKind(int kind) {
+ switch (kind) {
+ case IResourceDelta.ADDED:
+ return ResourceDeltaKind.ADDED;
+ case IResourceDelta.REMOVED:
+ return ResourceDeltaKind.REMOVED;
+ case IResourceDelta.CHANGED:
+ return ResourceDeltaKind.CHANGED;
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the resource type of the given url, and the resource name
+ *
+ * @param url the resource url to be parsed
+ * @return a pair of the resource type and the resource name
+ */
+ public static Pair<ResourceType,String> parseResource(String url) {
+ if (!url.startsWith("@")) { //$NON-NLS-1$
+ return null;
+ }
+ int typeEnd = url.indexOf('/', 1);
+ if (typeEnd == -1) {
+ return null;
+ }
+ int nameBegin = typeEnd + 1;
+
+ // Skip @ and @+
+ int typeBegin = url.startsWith("@+") ? 2 : 1; //$NON-NLS-1$
+
+ int colon = url.lastIndexOf(':', typeEnd);
+ if (colon != -1) {
+ typeBegin = colon + 1;
+ }
+ String typeName = url.substring(typeBegin, typeEnd);
+ ResourceType type = ResourceType.getEnum(typeName);
+ if (type == null) {
+ return null;
+ }
+ String name = url.substring(nameBegin);
+
+ return Pair.of(type, name);
+ }
+
+ /**
+ * Is this a resource that can be defined in any file within the "values" folder?
+ * <p>
+ * Some resource types can be defined <b>both</b> as a separate XML file as well
+ * as defined within a value XML file. This method will return true for these types
+ * as well. In other words, a ResourceType can return true for both
+ * {@link #isValueBasedResourceType} and {@link #isFileBasedResourceType}.
+ *
+ * @param type the resource type to check
+ * @return true if the given resource type can be represented as a value under the
+ * values/ folder
+ */
+ public static boolean isValueBasedResourceType(ResourceType type) {
+ List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
+ for (ResourceFolderType folderType : folderTypes) {
+ if (folderType == ResourceFolderType.VALUES) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Is this a resource that is defined in a file named by the resource plus the XML
+ * extension?
+ * <p>
+ * Some resource types can be defined <b>both</b> as a separate XML file as well as
+ * defined within a value XML file along with other properties. This method will
+ * return true for these resource types as well. In other words, a ResourceType can
+ * return true for both {@link #isValueBasedResourceType} and
+ * {@link #isFileBasedResourceType}.
+ *
+ * @param type the resource type to check
+ * @return true if the given resource type is stored in a file named by the resource
+ */
+ public static boolean isFileBasedResourceType(ResourceType type) {
+ List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
+ for (ResourceFolderType folderType : folderTypes) {
+ if (folderType != ResourceFolderType.VALUES) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if this class can create the given resource
+ *
+ * @param resource the resource to be created
+ * @return true if the {@link #createResource} method can create this resource
+ */
+ public static boolean canCreateResource(String resource) {
+ // Cannot create framework resources
+ if (resource.startsWith('@' + ANDROID_PKG + ':')) {
+ return false;
+ }
+
+ Pair<ResourceType,String> parsed = parseResource(resource);
+ if (parsed != null) {
+ ResourceType type = parsed.getFirst();
+ String name = parsed.getSecond();
+
+ // Make sure the name is valid
+ ResourceNameValidator validator =
+ ResourceNameValidator.create(false, (Set<String>) null /* existing */, type);
+ if (validator.isValid(name) != null) {
+ return false;
+ }
+
+ // We can create all value types
+ if (isValueBasedResourceType(type)) {
+ return true;
+ }
+
+ // We can create -some- file-based types - those supported by the New XML wizard:
+ for (ResourceFolderType folderType : FolderTypeRelationship.getRelatedFolders(type)) {
+ if (NewXmlFileWizard.canCreateXmlFile(folderType)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /** Creates a file-based resource, like a layout. Used by {@link #createResource} */
+ private static Pair<IFile,IRegion> createFileResource(IProject project, ResourceType type,
+ String name) {
+
+ ResourceFolderType folderType = null;
+ for (ResourceFolderType f : FolderTypeRelationship.getRelatedFolders(type)) {
+ if (NewXmlFileWizard.canCreateXmlFile(f)) {
+ folderType = f;
+ break;
+ }
+ }
+ if (folderType == null) {
+ return null;
+ }
+
+ // Find "dimens.xml" file in res/values/ (or corresponding name for other
+ // value types)
+ IPath projectPath = new Path(FD_RESOURCES + WS_SEP + folderType.getName() + WS_SEP
+ + name + '.' + EXT_XML);
+ IFile file = project.getFile(projectPath);
+ return NewXmlFileWizard.createXmlFile(project, file, folderType);
+ }
+
/**
- * Returns a formatted string usable in an XML to use the specified {@link ResourceItem}.
- * @param resourceItem The resource item.
- * @param system Whether this is a system resource or a project resource.
- * @return a string in the format @[type]/[name]
+ * Creates a resource of a given type, name and (if applicable) value
+ *
+ * @param project the project to contain the resource
+ * @param type the type of resource
+ * @param name the name of the resource
+ * @param value the value of the resource, if it is a value-type resource
+ * @return a pair of the file containing the resource and a region where the value
+ * appears
*/
- public static String getXmlString(ResourceType type, ResourceItem resourceItem,
- boolean system) {
- if (type == ResourceType.ID && resourceItem instanceof IIdResourceItem) {
- IIdResourceItem idResource = (IIdResourceItem)resourceItem;
- if (idResource.isDeclaredInline()) {
- return (system?"@android:":"@+") + type.getName() + "/" + resourceItem.getName(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ public static Pair<IFile,IRegion> createResource(IProject project, ResourceType type,
+ String name, String value) {
+ if (!isValueBasedResourceType(type)) {
+ return createFileResource(project, type, name);
+ }
+
+ // Find "dimens.xml" file in res/values/ (or corresponding name for other
+ // value types)
+ String fileName = type.getName() + 's';
+ String projectPath = FD_RESOURCES + WS_SEP + FD_RES_VALUES + WS_SEP
+ + fileName + '.' + EXT_XML;
+ Object editRequester = project;
+ IResource member = project.findMember(projectPath);
+ if (member != null) {
+ if (member instanceof IFile) {
+ IFile file = (IFile) member;
+ // File exists: Must add item to the XML
+ IModelManager manager = StructuredModelManager.getModelManager();
+ IStructuredModel model = null;
+ try {
+ model = manager.getExistingModelForEdit(file);
+ if (model == null) {
+ model = manager.getModelForEdit(file);
+ }
+ if (model instanceof IDOMModel) {
+ model.beginRecording(editRequester, String.format("Add %1$s",
+ type.getDisplayName()));
+ IDOMModel domModel = (IDOMModel) model;
+ Document document = domModel.getDocument();
+ Element root = document.getDocumentElement();
+ IStructuredDocument structuredDocument = model.getStructuredDocument();
+ Node lastElement = null;
+ NodeList childNodes = root.getChildNodes();
+ String indent = null;
+ for (int i = childNodes.getLength() - 1; i >= 0; i--) {
+ Node node = childNodes.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ lastElement = node;
+ indent = AndroidXmlEditor.getIndent(structuredDocument, node);
+ break;
+ }
+ }
+ if (indent == null || indent.length() == 0) {
+ indent = " "; //$NON-NLS-1$
+ }
+ Node nextChild = lastElement != null ? lastElement.getNextSibling() : null;
+ Text indentNode = document.createTextNode('\n' + indent);
+ root.insertBefore(indentNode, nextChild);
+ Element element = document.createElement(Hyperlinks.getTagName(type));
+ element.setAttribute(NAME_ATTR, name);
+ root.insertBefore(element, nextChild);
+ Text valueNode = document.createTextNode(value);
+ element.appendChild(valueNode);
+ model.save();
+ IndexedRegion domRegion = VisualRefactoring.getRegion(valueNode);
+ int startOffset = domRegion.getStartOffset();
+ int length = domRegion.getLength();
+ IRegion region = new Region(startOffset, length);
+ return Pair.of(file, region);
+ }
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Cannot access XML value model");
+ } finally {
+ if (model != null) {
+ model.endRecording(editRequester);
+ model.releaseFromEdit();
+ }
+ }
+ }
+
+ return null;
+ } else {
+ // No such file exists: just create it
+ String prolog = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$
+ StringBuilder sb = new StringBuilder(prolog);
+
+ String root = ResourcesDescriptors.ROOT_ELEMENT;
+ sb.append('<').append(root).append('>').append('\n');
+ sb.append(" "); //$NON-NLS-1$
+ sb.append('<');
+ sb.append(type.getName());
+ sb.append(" name=\""); //$NON-NLS-1$
+ sb.append(name);
+ sb.append('"');
+ sb.append('>');
+ int start = sb.length();
+ sb.append(value);
+ int end = sb.length();
+ sb.append('<').append('/');
+ sb.append(type.getName());
+ sb.append(">\n"); //$NON-NLS-1$
+ sb.append('<').append('/').append(root).append('>').append('\n');
+ String result = sb.toString();
+ String error = null;
+ try {
+ byte[] buf = result.getBytes("UTF8"); //$NON-NLS-1$
+ InputStream stream = new ByteArrayInputStream(buf);
+ IFile file = project.getFile(new Path(projectPath));
+ file.create(stream, true /*force*/, null /*progress*/);
+ IRegion region = new Region(start, end - start);
+ return Pair.of(file, region);
+ } catch (UnsupportedEncodingException e) {
+ error = e.getMessage();
+ } catch (CoreException e) {
+ error = e.getMessage();
}
+
+ error = String.format("Failed to generate %1$s: %2$s", name, error);
+ AdtPlugin.displayError("New Android XML File", error);
}
+ return null;
+ }
- return (system?"@android:":"@") + type.getName() + "/" + resourceItem.getName(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ /**
+ * Returns the theme name to be shown for theme styles, e.g. for "@style/Theme" it
+ * returns "Theme"
+ *
+ * @param style a theme style string
+ * @return the user visible theme name
+ */
+ public static String styleToTheme(String style) {
+ if (style.startsWith(PREFIX_STYLE)) {
+ style = style.substring(PREFIX_STYLE.length());
+ } else if (style.startsWith(PREFIX_ANDROID_STYLE)) {
+ style = style.substring(PREFIX_ANDROID_STYLE.length());
+ }
+ return style;
}
+ /**
+ * Returns the layout resource name for the given layout file, e.g. for
+ * /res/layout/foo.xml returns foo.
+ *
+ * @param layoutFile the layout file whose name we want to look up
+ * @return the layout name
+ */
+ public static String getLayoutName(IFile layoutFile) {
+ String layoutName = layoutFile.getName();
+ int dotIndex = layoutName.indexOf('.');
+ if (dotIndex != -1) {
+ layoutName = layoutName.substring(0, dotIndex);
+ }
+ return layoutName;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceItem.java
deleted file mode 100644
index c340ffe..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceItem.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.internal.resources;
-
-/**
- * Base class representing a Resource Item, as returned by a {@link IResourceRepository}.
- */
-public class ResourceItem implements Comparable<ResourceItem> {
-
- private final String mName;
-
- /**
- * Constructs a new ResourceItem
- * @param name the name of the resource as it appears in the XML and R.java files.
- */
- public ResourceItem(String name) {
- mName = name;
- }
-
- /**
- * Returns the name of the resource item.
- */
- public final String getName() {
- return mName;
- }
-
- /**
- * Compares the {@link ResourceItem} to another.
- * @param other the ResourceItem to be compared to.
- */
- public int compareTo(ResourceItem other) {
- return mName.compareTo(other.mName);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ResourceNameValidator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java
index cc1aa25..4c1127a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ResourceNameValidator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.wizards.newxmlfile;
+package com.android.ide.eclipse.adt.internal.resources;
-import static com.android.ide.eclipse.adt.AndroidConstants.DOT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResourceItem;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import org.eclipse.core.resources.IProject;
@@ -30,6 +31,7 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jface.dialogs.IInputValidator;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -40,12 +42,21 @@ public class ResourceNameValidator implements IInputValidator {
/** Set of existing names to check for conflicts with */
private Set<String> mExisting;
+ /**
+ * True if the resource name being considered is a "file" based resource (where the
+ * resource name is the actual file name, rather than just a value attribute inside an
+ * XML file name of arbitrary name
+ */
+ private boolean mIsFileType;
+
/** If true, allow .xml as a name suffix */
private boolean mAllowXmlExtension;
- private ResourceNameValidator(boolean allowXmlExtension, Set<String> existing) {
+ private ResourceNameValidator(boolean allowXmlExtension, Set<String> existing,
+ boolean isFileType) {
mAllowXmlExtension = allowXmlExtension;
mExisting = existing;
+ mIsFileType = isFileType;
}
public String isValid(String newText) {
@@ -59,7 +70,7 @@ public class ResourceNameValidator implements IInputValidator {
newText = newText.substring(0, newText.length() - DOT_XML.length());
}
- if (newText.indexOf('.') != -1 && !newText.endsWith(AndroidConstants.DOT_XML)) {
+ if (newText.indexOf('.') != -1 && !newText.endsWith(AdtConstants.DOT_XML)) {
return String.format("The filename must end with %1$s.", DOT_XML);
}
@@ -71,7 +82,19 @@ public class ResourceNameValidator implements IInputValidator {
for (int i = 1, n = newText.length(); i < n; i++) {
char c = newText.charAt(i);
if (!Character.isJavaIdentifierPart(c)) {
- return String.format("%1$c is not a valid resource name character", c);
+ return String.format("'%1$c' is not a valid resource name character", c);
+ }
+ }
+
+ if (mIsFileType) {
+ // AAPT only allows lowercase+digits+_:
+ // "%s: Invalid file name: must contain only [a-z0-9_.]","
+ for (int i = 0, n = newText.length(); i < n; i++) {
+ char c = newText.charAt(i);
+ if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')) {
+ return String.format(
+ "File-based resource names must contain only lowercase a-z, 0-9, or _.");
+ }
}
}
@@ -97,10 +120,13 @@ public class ResourceNameValidator implements IInputValidator {
*
* @param allowXmlExtension if true, allow .xml to be entered as a suffix for the
* resource name
+ * @param type the resource type of the resource name being validated
* @return a new {@link ResourceNameValidator}
*/
- public static ResourceNameValidator create(boolean allowXmlExtension) {
- return new ResourceNameValidator(allowXmlExtension, null);
+ public static ResourceNameValidator create(boolean allowXmlExtension,
+ ResourceFolderType type) {
+ boolean isFileType = type != ResourceFolderType.VALUES;
+ return new ResourceNameValidator(allowXmlExtension, null, isFileType);
}
/**
@@ -110,10 +136,13 @@ public class ResourceNameValidator implements IInputValidator {
* resource name
* @param existing An optional set of names that already exist (and therefore will not
* be considered valid if entered as the new name)
+ * @param type the resource type of the resource name being validated
* @return a new {@link ResourceNameValidator}
*/
- public static ResourceNameValidator create(boolean allowXmlExtension, Set<String> existing) {
- return new ResourceNameValidator(allowXmlExtension, existing);
+ public static ResourceNameValidator create(boolean allowXmlExtension, Set<String> existing,
+ ResourceType type) {
+ boolean isFileType = ResourceHelper.isFileBasedResourceType(type);
+ return new ResourceNameValidator(allowXmlExtension, existing, isFileType);
}
/**
@@ -130,11 +159,12 @@ public class ResourceNameValidator implements IInputValidator {
Set<String> existing = new HashSet<String>();
ResourceManager manager = ResourceManager.getInstance();
ProjectResources projectResources = manager.getProjectResources(project);
- ProjectResourceItem[] resources = projectResources.getResources(type);
- for (ProjectResourceItem resource : resources) {
- existing.add(resource.getName());
+ Collection<ResourceItem> items = projectResources.getResourceItemsOfType(type);
+ for (ResourceItem item : items) {
+ existing.add(item.getName());
}
- return new ResourceNameValidator(allowXmlExtension, existing);
+ boolean isFileType = ResourceHelper.isFileBasedResourceType(type);
+ return new ResourceNameValidator(allowXmlExtension, existing, isFileType);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java
index 295fd4c..172f471 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java
@@ -16,8 +16,9 @@
package com.android.ide.eclipse.adt.internal.resources.manager;
+import com.android.ide.common.resources.IntArrayWrapper;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener;
@@ -77,7 +78,7 @@ public final class CompiledResourcesMonitor implements IFileListener, IProjectLi
* @see IFileListener#fileChanged
*/
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
- if (file.getName().equals(AndroidConstants.FN_COMPILED_RESOURCE_CLASS)) {
+ if (file.getName().equals(AdtConstants.FN_COMPILED_RESOURCE_CLASS)) {
loadAndParseRClass(file.getProject());
}
}
@@ -115,7 +116,7 @@ public final class CompiledResourcesMonitor implements IFileListener, IProjectLi
public void projectOpenedWithWorkspace(IProject project) {
try {
// check this is an android project
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT)) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT)) {
loadAndParseRClass(project);
}
} catch (CoreException e) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ConfigurableResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ConfigurableResourceItem.java
deleted file mode 100644
index 2a998f8..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ConfigurableResourceItem.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.internal.resources.manager;
-
-/**
- * Represents a resource item that can exist in multiple "alternate" versions.
- */
-public class ConfigurableResourceItem extends ProjectResourceItem {
-
- /**
- * Constructs a new Resource Item.
- * @param name the name of the resource as it appears in the XML and R.java files.
- */
- public ConfigurableResourceItem(String name) {
- super(name);
- }
-
- /**
- * Returns if the resource item has at least one non-default configuration.
- */
- public boolean hasAlternates() {
- for (ResourceFile file : mFiles) {
- if (file.getFolder().getConfiguration().isDefault() == false) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Returns whether the resource has a default version, with no qualifier.
- */
- public boolean hasDefault() {
- for (ResourceFile file : mFiles) {
- if (file.getFolder().getConfiguration().isDefault()) {
- return true;
- }
- }
-
- // We only want to return false if there's no default and more than 0 items.
- return (mFiles.size() == 0);
- }
-
- /**
- * Returns the number of alternate versions of this resource.
- */
- public int getAlternateCount() {
- int count = 0;
- for (ResourceFile file : mFiles) {
- if (file.getFolder().getConfiguration().isDefault() == false) {
- count++;
- }
- }
-
- return count;
- }
-
- /*
- * (non-Javadoc)
- * Returns whether the item can be edited directly (ie it does not have alternate versions).
- */
- @Override
- public boolean isEditableDirectly() {
- return hasAlternates() == false;
- }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FolderTypeRelationship.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FolderTypeRelationship.java
deleted file mode 100644
index 168f7fb..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FolderTypeRelationship.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.internal.resources.manager;
-
-import com.android.resources.ResourceType;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Set;
-
-/**
- * This class gives access to the bi directional relationship between {@link ResourceType} and
- * {@link ResourceFolderType}.
- */
-public final class FolderTypeRelationship {
-
- private final static HashMap<ResourceType, ResourceFolderType[]> mTypeToFolderMap =
- new HashMap<ResourceType, ResourceFolderType[]>();
-
- private final static HashMap<ResourceFolderType, ResourceType[]> mFolderToTypeMap =
- new HashMap<ResourceFolderType, ResourceType[]>();
-
- // generate the relationships.
- static {
- HashMap<ResourceType, List<ResourceFolderType>> typeToFolderMap =
- new HashMap<ResourceType, List<ResourceFolderType>>();
-
- HashMap<ResourceFolderType, List<ResourceType>> folderToTypeMap =
- new HashMap<ResourceFolderType, List<ResourceType>>();
-
- add(ResourceType.ANIM, ResourceFolderType.ANIM, typeToFolderMap, folderToTypeMap);
- add(ResourceType.ANIMATOR, ResourceFolderType.ANIMATOR, typeToFolderMap, folderToTypeMap);
- add(ResourceType.ARRAY, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.ATTR, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.BOOL, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.COLOR, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.COLOR, ResourceFolderType.COLOR, typeToFolderMap, folderToTypeMap);
- add(ResourceType.DECLARE_STYLEABLE, ResourceFolderType.VALUES, typeToFolderMap,
- folderToTypeMap);
- add(ResourceType.DIMEN, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.DRAWABLE, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.DRAWABLE, ResourceFolderType.DRAWABLE, typeToFolderMap, folderToTypeMap);
- add(ResourceType.FRACTION, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.ID, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.INTEGER, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.INTERPOLATOR, ResourceFolderType.INTERPOLATOR, typeToFolderMap,
- folderToTypeMap);
- add(ResourceType.LAYOUT, ResourceFolderType.LAYOUT, typeToFolderMap, folderToTypeMap);
- add(ResourceType.MENU, ResourceFolderType.MENU, typeToFolderMap, folderToTypeMap);
- add(ResourceType.MIPMAP, ResourceFolderType.MIPMAP, typeToFolderMap, folderToTypeMap);
- add(ResourceType.PLURALS, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.PUBLIC, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.RAW, ResourceFolderType.RAW, typeToFolderMap, folderToTypeMap);
- add(ResourceType.STRING, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.STYLE, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.STYLEABLE, ResourceFolderType.VALUES, typeToFolderMap, folderToTypeMap);
- add(ResourceType.XML, ResourceFolderType.XML, typeToFolderMap, folderToTypeMap);
-
- optimize(typeToFolderMap, folderToTypeMap);
- }
-
- /**
- * Returns a list of {@link ResourceType}s that can be generated from files inside a folder
- * of the specified type.
- * @param folderType The folder type.
- * @return an array of {@link ResourceType}
- */
- public static ResourceType[] getRelatedResourceTypes(ResourceFolderType folderType) {
- ResourceType[] array = mFolderToTypeMap.get(folderType);
- if (array != null) {
- return array;
- }
- return new ResourceType[0];
- }
-
- /**
- * Returns a list of {@link ResourceFolderType} that can contain files generating resources
- * of the specified type.
- * @param resType the type of resource.
- * @return an array of {@link ResourceFolderType}
- */
- public static ResourceFolderType[] getRelatedFolders(ResourceType resType) {
- ResourceFolderType[] array = mTypeToFolderMap.get(resType);
- if (array != null) {
- return array;
- }
- return new ResourceFolderType[0];
- }
-
- /**
- * Returns true if the {@link ResourceType} and the {@link ResourceFolderType} values match.
- * @param resType the resource type.
- * @param folderType the folder type.
- * @return true if files inside the folder of the specified {@link ResourceFolderType}
- * could generate a resource of the specified {@link ResourceType}
- */
- public static boolean match(ResourceType resType, ResourceFolderType folderType) {
- ResourceFolderType[] array = mTypeToFolderMap.get(resType);
-
- if (array != null && array.length > 0) {
- for (ResourceFolderType fType : array) {
- if (fType == folderType) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Adds a {@link ResourceType} - {@link ResourceFolderType} relationship. this indicates that
- * a file in the folder can generate a resource of the specified type.
- * @param type The resourceType
- * @param folder The {@link ResourceFolderType}
- * @param folderToTypeMap
- * @param typeToFolderMap
- */
- private static void add(ResourceType type, ResourceFolderType folder,
- HashMap<ResourceType, List<ResourceFolderType>> typeToFolderMap,
- HashMap<ResourceFolderType, List<ResourceType>> folderToTypeMap) {
- // first we add the folder to the list associated with the type.
- List<ResourceFolderType> folderList = typeToFolderMap.get(type);
- if (folderList == null) {
- folderList = new ArrayList<ResourceFolderType>();
- typeToFolderMap.put(type, folderList);
- }
- if (folderList.indexOf(folder) == -1) {
- folderList.add(folder);
- }
-
- // now we add the type to the list associated with the folder.
- List<ResourceType> typeList = folderToTypeMap.get(folder);
- if (typeList == null) {
- typeList = new ArrayList<ResourceType>();
- folderToTypeMap.put(folder, typeList);
- }
- if (typeList.indexOf(type) == -1) {
- typeList.add(type);
- }
- }
-
- /**
- * Optimize the map to contains array instead of lists (since the api returns arrays)
- * @param typeToFolderMap
- * @param folderToTypeMap
- */
- private static void optimize(HashMap<ResourceType, List<ResourceFolderType>> typeToFolderMap,
- HashMap<ResourceFolderType, List<ResourceType>> folderToTypeMap) {
- Set<ResourceType> types = typeToFolderMap.keySet();
- for (ResourceType type : types) {
- List<ResourceFolderType> list = typeToFolderMap.get(type);
- mTypeToFolderMap.put(type, list.toArray(new ResourceFolderType[list.size()]));
- }
-
- Set<ResourceFolderType> folders = folderToTypeMap.keySet();
- for (ResourceFolderType folder : folders) {
- List<ResourceType> list = folderToTypeMap.get(folder);
- mFolderToTypeMap.put(folder, list.toArray(new ResourceType[list.size()]));
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java
index c6d16d4..cc615ec 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java
@@ -16,6 +16,8 @@
package com.android.ide.eclipse.adt.internal.resources.manager;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceFolder;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdResourceItem.java
deleted file mode 100644
index 8b142fb..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdResourceItem.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.internal.resources.manager;
-
-import com.android.ide.eclipse.adt.internal.resources.IIdResourceItem;
-import com.android.resources.ResourceType;
-
-/**
- * Represents a resource item of type {@link ResourceType#ID}
- */
-public class IdResourceItem extends ProjectResourceItem implements IIdResourceItem {
-
- private final boolean mIsDeclaredInline;
-
- /**
- * Constructs a new ResourceItem.
- * @param name the name of the resource as it appears in the XML and R.java files.
- * @param isDeclaredInline Whether this id was declared inline.
- */
- IdResourceItem(String name, boolean isDeclaredInline) {
- super(name);
- mIsDeclaredInline = isDeclaredInline;
- }
-
- /*
- * (non-Javadoc)
- * Returns whether the ID resource has been declared inline inside another resource XML file.
- */
- public boolean isDeclaredInline() {
- return mIsDeclaredInline;
- }
-
- /* (non-Javadoc)
- * Returns whether the item can be edited (ie, the id was not declared inline).
- */
- @Override
- public boolean isEditableDirectly() {
- return !mIsDeclaredInline;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java
index 0deb89c..dc543b8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.resources.manager;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.build.BuildHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
@@ -262,7 +262,7 @@ public final class ProjectClassLoader extends ClassLoader {
IPath path = e.getPath();
// check the name ends with .jar
- if (AndroidConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
+ if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
boolean local = false;
IResource resource = wsRoot.findMember(path);
if (resource != null && resource.exists() &&
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResourceItem.java
deleted file mode 100644
index 845a974..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResourceItem.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package com.android.ide.eclipse.adt.internal.resources.manager;
-
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.resources.ResourceType;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Base class for Resource Item coming from an Android Project.
- */
-public abstract class ProjectResourceItem extends ResourceItem {
-
- private final static Comparator<ResourceFile> sComparator = new Comparator<ResourceFile>() {
- public int compare(ResourceFile file1, ResourceFile file2) {
- // get both FolderConfiguration and compare them
- FolderConfiguration fc1 = file1.getFolder().getConfiguration();
- FolderConfiguration fc2 = file2.getFolder().getConfiguration();
-
- return fc1.compareTo(fc2);
- }
- };
-
- /**
- * List of files generating this ResourceItem.
- */
- protected final ArrayList<ResourceFile> mFiles = new ArrayList<ResourceFile>();
-
- /**
- * Constructs a new ResourceItem.
- * @param name the name of the resource as it appears in the XML and R.java files.
- */
- public ProjectResourceItem(String name) {
- super(name);
- }
-
- /**
- * Returns whether the resource item is editable directly.
- * <p/>
- * This is typically the case for resources that don't have alternate versions, or resources
- * of type {@link ResourceType#ID} that aren't declared inline.
- */
- public abstract boolean isEditableDirectly();
-
- /**
- * Adds a new version of this resource item, by adding its {@link ResourceFile}.
- * @param file the {@link ResourceFile} object.
- */
- protected void add(ResourceFile file) {
- mFiles.add(file);
- }
-
- /**
- * Reset the item by emptying its version list.
- */
- protected void reset() {
- mFiles.clear();
- }
-
- /**
- * Returns the sorted list of {@link ResourceItem} objects for this resource item.
- */
- public ResourceFile[] getSourceFileArray() {
- ArrayList<ResourceFile> list = new ArrayList<ResourceFile>();
- list.addAll(mFiles);
-
- Collections.sort(list, sComparator);
-
- return list.toArray(new ResourceFile[list.size()]);
- }
-
- /**
- * Returns the list of {@link ResourceItem} objects for this resource item.
- */
- public List<ResourceFile> getSourceFileList() {
- return Collections.unmodifiableList(mFiles);
- }
-
-
- /**
- * Replaces the content of the receiver with the ResourceItem received as parameter.
- * @param item
- */
- protected void replaceWith(ProjectResourceItem item) {
- mFiles.clear();
- mFiles.addAll(item.mFiles);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
index 64a36e1..ec8b717 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
@@ -17,48 +17,44 @@
package com.android.ide.eclipse.adt.internal.resources.manager;
import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
+import com.android.ide.common.resources.InlineResourceItem;
+import com.android.ide.common.resources.IntArrayWrapper;
+import com.android.ide.common.resources.ResourceFolder;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.io.IFolderWrapper;
import com.android.resources.ResourceType;
-import com.android.sdklib.io.IAbstractFolder;
import com.android.util.Pair;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
import java.util.Map.Entry;
/**
- * Represents the resources of a project. This is a file view of the resources, with handling
- * for the alternate resource types. For a compiled view use CompiledResources.
+ * Represents the resources of a project.
+ * On top of the regular {@link ResourceRepository} features it provides:
+ *<ul>
+ *<li>configured resources contain the resources coming from the libraries.</li>
+ *<li>resolution to and from resource integer (compiled value in R.java).</li>
+ *<li>handles resource integer for non existing values of type ID. This is used when rendering.</li>
+ *<li>layouts that have no been saved yet. This is handled by generating dynamic IDs
+ * on the fly.</li>
+ *</ul>
*/
-public class ProjectResources implements IResourceRepository {
- private final static int DYNAMIC_ID_SEED_START = 0; // this should not conflict with any
- // project IDs that start at a much higher
- // value
-
- private final Map<ResourceFolderType, List<ResourceFolder>> mFolderMap =
- new EnumMap<ResourceFolderType, List<ResourceFolder>>(ResourceFolderType.class);
-
- private final Map<ResourceType, List<ProjectResourceItem>> mResourceMap =
- new EnumMap<ResourceType, List<ProjectResourceItem>>(ResourceType.class);
+public class ProjectResources extends ResourceRepository {
+ // project resources are defined as 0x7FXX#### where XX is the resource type (layout, drawable,
+ // etc...). Using FF as the type allows for 255 resource types before we get a collision
+ // which should be fine.
+ private final static int DYNAMIC_ID_SEED_START = 0x7fff0000;
/** Map of (name, id) for resources of type {@link ResourceType#ID} coming from R.java */
private Map<ResourceType, Map<String, Integer>> mResourceValueMap;
@@ -67,352 +63,39 @@ public class ProjectResources implements IResourceRepository {
/** Map of (int[], name) for styleable resources coming from R.java */
private Map<IntArrayWrapper, String> mStyleableValueToNameMap;
+ /**
+ * This list is used by {@link #getResourceId(String, String)} when the resource
+ * query is an ID that doesn't exist (for example for ID automatically generated in
+ * layout files that are not saved yet).
+ */
private final Map<String, Integer> mDynamicIds = new HashMap<String, Integer>();
+ private final Map<Integer, String> mRevDynamicIds = new HashMap<Integer, String>();
private int mDynamicSeed = DYNAMIC_ID_SEED_START;
- /** Cached list of {@link IdResourceItem}. This is mix of IdResourceItem created by
- * {@link MultiResourceFile} for ids coming from XML files under res/values and
- * {@link IdResourceItem} created manually, from the list coming from R.java */
- private final List<IdResourceItem> mIdResourceList = new ArrayList<IdResourceItem>();
-
- private final boolean mIsFrameworkRepository;
private final IProject mProject;
- private final IntArrayWrapper mWrapper = new IntArrayWrapper(null);
-
/**
* Makes a ProjectResources for a given <var>project</var>.
* @param project the project.
*/
public ProjectResources(IProject project) {
- mIsFrameworkRepository = false;
+ super(false /*isFrameworkRepository*/);
mProject = project;
}
/**
- * Makes a ProjectResource for a framework repository.
+ * Returns the resources values matching a given {@link FolderConfiguration}, this will
+ * include library dependency.
*
- * @see #isSystemRepository()
- */
- public ProjectResources() {
- mIsFrameworkRepository = true;
- mProject = null;
- }
-
- /**
- * Returns whether this ProjectResources is for a project or for a framework.
- */
- public boolean isSystemRepository() {
- return mIsFrameworkRepository;
- }
-
- /**
- * Adds a Folder Configuration to the project.
- * @param type The resource type.
- * @param config The resource configuration.
- * @param folder The workspace folder object.
- * @return the {@link ResourceFolder} object associated to this folder.
- */
- protected ResourceFolder add(ResourceFolderType type, FolderConfiguration config,
- IAbstractFolder folder) {
- // get the list for the resource type
- List<ResourceFolder> list = mFolderMap.get(type);
-
- if (list == null) {
- list = new ArrayList<ResourceFolder>();
-
- ResourceFolder cf = new ResourceFolder(type, config, folder, mIsFrameworkRepository);
- list.add(cf);
-
- mFolderMap.put(type, list);
-
- return cf;
- }
-
- // look for an already existing folder configuration.
- for (ResourceFolder cFolder : list) {
- if (cFolder.mConfiguration.equals(config)) {
- // config already exist. Nothing to be done really, besides making sure
- // the IFolder object is up to date.
- cFolder.mFolder = folder;
- return cFolder;
- }
- }
-
- // If we arrive here, this means we didn't find a matching configuration.
- // So we add one.
- ResourceFolder cf = new ResourceFolder(type, config, folder, mIsFrameworkRepository);
- list.add(cf);
-
- return cf;
- }
-
- /**
- * Removes a {@link ResourceFolder} associated with the specified {@link IAbstractFolder}.
- * @param type The type of the folder
- * @param folder the IFolder object.
- * @return the {@link ResourceFolder} that was removed, or null if no matches were found.
- */
- protected ResourceFolder removeFolder(ResourceFolderType type, IFolder folder) {
- // get the list of folders for the resource type.
- List<ResourceFolder> list = mFolderMap.get(type);
-
- if (list != null) {
- int count = list.size();
- for (int i = 0 ; i < count ; i++) {
- ResourceFolder resFolder = list.get(i);
- // this is only used for Eclipse stuff so we know it's an IFolderWrapper
- IFolderWrapper wrapper = (IFolderWrapper) resFolder.getFolder();
- if (wrapper.getIFolder().equals(folder)) {
- // we found the matching ResourceFolder. we need to remove it.
- list.remove(i);
-
- // we now need to invalidate this resource type.
- // The easiest way is to touch one of the other folders of the same type.
- if (list.size() > 0) {
- list.get(0).touch();
- } else {
- // if the list is now empty, and we have a single ResouceType out of this
- // ResourceFolderType, then we are done.
- // However, if another ResourceFolderType can generate similar ResourceType
- // than this, we need to update those ResourceTypes as well.
- // For instance, if the last "drawable-*" folder is deleted, we need to
- // refresh the ResourceItem associated with ResourceType.DRAWABLE.
- // Those can be found in ResourceFolderType.DRAWABLE but also in
- // ResourceFolderType.VALUES.
- // If we don't find a single folder to touch, then it's fine, as the top
- // level items (the list of generated resource types) is not cached
- // (for now)
-
- // get the lists of ResourceTypes generated by this ResourceFolderType
- ResourceType[] resTypes = FolderTypeRelationship.getRelatedResourceTypes(
- type);
-
- // for each of those, make sure to find one folder to touch so that the
- // list of ResourceItem associated with the type is rebuilt.
- for (ResourceType resType : resTypes) {
- // get the list of folder that can generate this type
- ResourceFolderType[] folderTypes =
- FolderTypeRelationship.getRelatedFolders(resType);
-
- // we only need to touch one folder in any of those (since it's one
- // folder per type, not per folder type).
- for (ResourceFolderType folderType : folderTypes) {
- List<ResourceFolder> resFolders = mFolderMap.get(folderType);
-
- if (resFolders != null && resFolders.size() > 0) {
- resFolders.get(0).touch();
- break;
- }
- }
- }
- }
-
- // we're done updating/touching, we can stop
- return resFolder;
- }
- }
- }
-
- return null;
- }
-
-
- /**
- * Returns a list of {@link ResourceFolder} for a specific {@link ResourceFolderType}.
- * @param type The {@link ResourceFolderType}
- */
- public List<ResourceFolder> getFolders(ResourceFolderType type) {
- return mFolderMap.get(type);
- }
-
- /* (non-Javadoc)
- * @see com.android.ide.eclipse.editors.resources.IResourceRepository#getAvailableResourceTypes()
- */
- public ResourceType[] getAvailableResourceTypes() {
- ArrayList<ResourceType> list = new ArrayList<ResourceType>();
-
- // For each key, we check if there's a single ResourceType match.
- // If not, we look for the actual content to give us the resource type.
-
- for (ResourceFolderType folderType : mFolderMap.keySet()) {
- ResourceType types[] = FolderTypeRelationship.getRelatedResourceTypes(folderType);
- if (types.length == 1) {
- // before we add it we check if it's not already present, since a ResourceType
- // could be created from multiple folders, even for the folders that only create
- // one type of resource (drawable for instance, can be created from drawable/ and
- // values/)
- if (list.indexOf(types[0]) == -1) {
- list.add(types[0]);
- }
- } else {
- // there isn't a single resource type out of this folder, so we look for all
- // content.
- List<ResourceFolder> folders = mFolderMap.get(folderType);
- if (folders != null) {
- for (ResourceFolder folder : folders) {
- Collection<ResourceType> folderContent = folder.getResourceTypes();
-
- // then we add them, but only if they aren't already in the list.
- for (ResourceType folderResType : folderContent) {
- if (list.indexOf(folderResType) == -1) {
- list.add(folderResType);
- }
- }
- }
- }
- }
- }
-
- // in case ResourceType.ID haven't been added yet because there's no id defined
- // in XML, we check on the list of compiled id resources.
- if (list.indexOf(ResourceType.ID) == -1 && mResourceValueMap != null) {
- Map<String, Integer> map = mResourceValueMap.get(ResourceType.ID);
- if (map != null && map.size() > 0) {
- list.add(ResourceType.ID);
- }
- }
-
- // at this point the list is full of ResourceType defined in the files.
- // We need to sort it.
- Collections.sort(list);
-
- return list.toArray(new ResourceType[list.size()]);
- }
-
- /* (non-Javadoc)
- * @see com.android.ide.eclipse.editors.resources.IResourceRepository#getResources(com.android.ide.eclipse.common.resources.ResourceType)
- */
- public ProjectResourceItem[] getResources(ResourceType type) {
- checkAndUpdate(type);
-
- if (type == ResourceType.ID) {
- synchronized (mIdResourceList) {
- return mIdResourceList.toArray(new ProjectResourceItem[mIdResourceList.size()]);
- }
- }
-
- List<ProjectResourceItem> items = mResourceMap.get(type);
-
- return items.toArray(new ProjectResourceItem[items.size()]);
- }
-
- /* (non-Javadoc)
- * @see com.android.ide.eclipse.editors.resources.IResourceRepository#hasResources(com.android.ide.eclipse.common.resources.ResourceType)
- */
- public boolean hasResources(ResourceType type) {
- checkAndUpdate(type);
-
- if (type == ResourceType.ID) {
- synchronized (mIdResourceList) {
- return mIdResourceList.size() > 0;
- }
- }
-
- List<ProjectResourceItem> items = mResourceMap.get(type);
- return (items != null && items.size() > 0);
- }
-
- /**
- * Returns the {@link ResourceFolder} associated with a {@link IFolder}.
- * @param folder The {@link IFolder} object.
- * @return the {@link ResourceFolder} or null if it was not found.
- */
- public ResourceFolder getResourceFolder(IFolder folder) {
- for (List<ResourceFolder> list : mFolderMap.values()) {
- for (ResourceFolder resFolder : list) {
- // this is only used for Eclipse stuff so we know it's an IFolderWrapper
- IFolderWrapper wrapper = (IFolderWrapper) resFolder.getFolder();
- if (wrapper.getIFolder().equals(folder)) {
- return resFolder;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Returns the {@link ResourceFile} matching the given name, {@link ResourceFolderType} and
- * configuration.
- * <p/>This only works with files generating one resource named after the file (for instance,
- * layouts, bitmap based drawable, xml, anims).
- * @return the matching file or <code>null</code> if no match was found.
- */
- public ResourceFile getMatchingFile(String name, ResourceFolderType type,
- FolderConfiguration config) {
- // get the folders for the given type
- List<ResourceFolder> folders = mFolderMap.get(type);
-
- // look for folders containing a file with the given name.
- ArrayList<ResourceFolder> matchingFolders = new ArrayList<ResourceFolder>();
-
- // remove the folders that do not have a file with the given name.
- for (int i = 0 ; i < folders.size(); i++) {
- ResourceFolder folder = folders.get(i);
-
- if (folder.hasFile(name) == true) {
- matchingFolders.add(folder);
- }
- }
-
- // from those, get the folder with a config matching the given reference configuration.
- Resource match = findMatchingConfiguredResource(matchingFolders, config);
-
- // do we have a matching folder?
- if (match instanceof ResourceFolder) {
- // get the ResourceFile from the filename
- return ((ResourceFolder)match).getFile(name);
- }
-
- return null;
- }
-
- /**
- * Returns the list of source files for a given resource.
- * Optionally, if a {@link FolderConfiguration} is given, then only the best
- * match for this config is returned.
- *
- * @param type the type of the resource.
- * @param name the name of the resource.
- * @param referenceConfig an optional config for which only the best match will be returned.
- *
- * @return a list of files generating this resource or null if it was not found.
- */
- public List<ResourceFile> getSourceFiles(ResourceType type, String name,
- FolderConfiguration referenceConfig) {
-
- ProjectResourceItem[] resources = getResources(type);
-
- for (ProjectResourceItem item : resources) {
- if (name.equals(item.getName())) {
- if (referenceConfig != null) {
- Resource match = findMatchingConfiguredResource(item.getSourceFileList(),
- referenceConfig);
- if (match instanceof ResourceFile) {
- ArrayList<ResourceFile> list = new ArrayList<ResourceFile>();
- list.add((ResourceFile) match);
- return list;
- }
-
- return null;
- }
- return item.getSourceFileList();
- }
- }
-
- return null;
- }
-
- /**
- * Returns the resources values matching a given {@link FolderConfiguration}.
* @param referenceConfig the configuration that each value must match.
+ * @return a map with guaranteed to contain an entry for each {@link ResourceType}
*/
+ @Override
public Map<ResourceType, Map<String, ResourceValue>> getConfiguredResources(
FolderConfiguration referenceConfig) {
- Map<ResourceType, Map<String, ResourceValue>> map =
+ Map<ResourceType, Map<String, ResourceValue>> resultMap =
new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class);
// if the project contains libraries, we need to add the libraries resources here
@@ -433,25 +116,27 @@ public class ProjectResources implements IResourceRepository {
ProjectResources libRes = resMgr.getProjectResources(library);
if (libRes != null) {
- // make sure they are loaded
- libRes.loadAll();
+ // get the library resources, and only the library, not the dependencies
+ // so call doGetConfiguredResources() directly.
+ Map<ResourceType, Map<String, ResourceValue>> libMap =
+ libRes.doGetConfiguredResources(referenceConfig);
// we don't want to simply replace the whole map, but instead merge the
// content of any sub-map
- Map<ResourceType, Map<String, ResourceValue>> libMap =
- libRes.getConfiguredResources(referenceConfig);
+ for (Entry<ResourceType, Map<String, ResourceValue>> libEntry :
+ libMap.entrySet()) {
- for (Entry<ResourceType, Map<String, ResourceValue>> entry : libMap.entrySet()) {
// get the map currently in the result map for this resource type
- Map<String, ResourceValue> tempMap = map.get(entry.getKey());
+ Map<String, ResourceValue> tempMap = resultMap.get(libEntry.getKey());
if (tempMap == null) {
// since there's no current map for this type, just add the map
// directly coming from the library resources
- map.put(entry.getKey(), entry.getValue());
+ resultMap.put(libEntry.getKey(), libEntry.getValue());
} else {
// already a map for this type. add the resources from the
- // library.
- tempMap.putAll(entry.getValue());
+ // library, this will override existing value, which is why
+ // we loop in a specific library order.
+ tempMap.putAll(libEntry.getValue());
}
}
}
@@ -460,81 +145,59 @@ public class ProjectResources implements IResourceRepository {
}
// now the project resources themselves.
- // Don't blindly fill the map, instead check if there are sub-map already present
- // due to library resources.
-
- // special case for Id since there's a mix of compiled id (declared inline) and id declared
- // in the XML files.
- if (mIdResourceList.size() > 0) {
- Map<String, ResourceValue> idMap = map.get(ResourceType.ID);
-
- if (idMap == null) {
- idMap = new HashMap<String, ResourceValue>();
- map.put(ResourceType.ID, idMap);
- }
- for (IdResourceItem id : mIdResourceList) {
- // FIXME: cache the ResourceValue!
- idMap.put(id.getName(), new ResourceValue(ResourceType.ID, id.getName(),
- mIsFrameworkRepository));
- }
-
- }
-
- Set<ResourceType> keys = mResourceMap.keySet();
- for (ResourceType key : keys) {
- // we don't process ID resources since we already did it above.
- if (key != ResourceType.ID) {
- // get the local results
- Map<String, ResourceValue> localResMap = getConfiguredResource(key,
- referenceConfig);
-
- // check if a map for this type already exists
- Map<String, ResourceValue> resMap = map.get(key);
- if (resMap == null) {
- // just use the local results.
- map.put(key, localResMap);
- } else {
- // add to the library results.
- resMap.putAll(localResMap);
- }
+ Map<ResourceType, Map<String, ResourceValue>> thisProjectMap =
+ doGetConfiguredResources(referenceConfig);
+
+ // now merge the maps.
+ for (Entry<ResourceType, Map<String, ResourceValue>> entry : thisProjectMap.entrySet()) {
+ ResourceType type = entry.getKey();
+ Map<String, ResourceValue> typeMap = resultMap.get(type);
+ if (typeMap == null) {
+ resultMap.put(type, entry.getValue());
+ } else {
+ typeMap.putAll(entry.getValue());
}
}
- return map;
+ return resultMap;
}
/**
- * Loads all the resources. Essentially this forces to load the values from the
- * {@link ResourceFile} objects to make sure they are up to date and loaded
- * in {@link #mResourceMap}.
+ * Returns the {@link ResourceFolder} associated with a {@link IFolder}.
+ * @param folder The {@link IFolder} object.
+ * @return the {@link ResourceFolder} or null if it was not found.
+ *
+ * @see ResourceRepository#getResourceFolder(com.android.io.IAbstractFolder)
*/
- public void loadAll() {
- // gets all the resource types available.
- ResourceType[] types = getAvailableResourceTypes();
-
- // loop on them and load them
- for (ResourceType type: types) {
- checkAndUpdate(type);
- }
+ public ResourceFolder getResourceFolder(IFolder folder) {
+ return getResourceFolder(new IFolderWrapper(folder));
}
/**
* Resolves a compiled resource id into the resource name and type
- * @param id
- * @return an array of 2 strings { name, type } or null if the id could not be resolved
+ * @param id the resource integer id.
+ * @return a {@link Pair} of 2 strings { name, type } or null if the id could not be resolved
*/
- public Pair<ResourceType, String> resolveResourceValue(int id) {
+ public Pair<ResourceType, String> resolveResourceId(int id) {
+ Pair<ResourceType, String> result = null;
if (mResIdValueToNameMap != null) {
- return mResIdValueToNameMap.get(id);
+ result = mResIdValueToNameMap.get(id);
}
- return null;
+ if (result == null) {
+ String name = mRevDynamicIds.get(id);
+ if (name != null) {
+ result = Pair.of(ResourceType.ID, name);
+ }
+ }
+
+ return result;
}
/**
- * Resolves a compiled resource id of type int[] into the resource name.
+ * Resolves a compiled styleable id of type int[] into the styleable name.
*/
- public String resolveResourceValue(int[] id) {
+ public String resolveStyleable(int[] id) {
if (mStyleableValueToNameMap != null) {
mWrapper.set(id);
return mStyleableValueToNameMap.get(mWrapper);
@@ -544,12 +207,12 @@ public class ProjectResources implements IResourceRepository {
}
/**
- * Returns the value of a resource by its type and name.
+ * Returns the integer id of a resource given its type and name.
* <p/>If the resource is of type {@link ResourceType#ID} and does not exist in the
* internal map, then new id values are dynamically generated (and stored so that queries
* with the same names will return the same value).
*/
- public Integer getResourceValue(ResourceType type, String name) {
+ public Integer getResourceId(ResourceType type, String name) {
if (mResourceValueMap != null) {
Map<String, Integer> map = mResourceValueMap.get(type);
if (map != null) {
@@ -570,327 +233,41 @@ public class ProjectResources implements IResourceRepository {
}
/**
- * Returns the sorted list of languages used in the resources.
- */
- public SortedSet<String> getLanguages() {
- SortedSet<String> set = new TreeSet<String>();
-
- Collection<List<ResourceFolder>> folderList = mFolderMap.values();
- for (List<ResourceFolder> folderSubList : folderList) {
- for (ResourceFolder folder : folderSubList) {
- FolderConfiguration config = folder.getConfiguration();
- LanguageQualifier lang = config.getLanguageQualifier();
- if (lang != null) {
- set.add(lang.getShortDisplayValue());
- }
- }
- }
-
- return set;
- }
-
- /**
- * Returns the sorted list of regions used in the resources with the given language.
- * @param currentLanguage the current language the region must be associated with.
- */
- public SortedSet<String> getRegions(String currentLanguage) {
- SortedSet<String> set = new TreeSet<String>();
-
- Collection<List<ResourceFolder>> folderList = mFolderMap.values();
- for (List<ResourceFolder> folderSubList : folderList) {
- for (ResourceFolder folder : folderSubList) {
- FolderConfiguration config = folder.getConfiguration();
-
- // get the language
- LanguageQualifier lang = config.getLanguageQualifier();
- if (lang != null && lang.getShortDisplayValue().equals(currentLanguage)) {
- RegionQualifier region = config.getRegionQualifier();
- if (region != null) {
- set.add(region.getShortDisplayValue());
- }
- }
- }
- }
-
- return set;
- }
-
- /**
* Resets the list of dynamic Ids. This list is used by
- * {@link #getResourceValue(String, String)} when the resource query is an ID that doesn't
- * exist (for example for ID automatically generated in layout files that are not saved.
+ * {@link #getResourceId(String, String)} when the resource query is an ID that doesn't
+ * exist (for example for ID automatically generated in layout files that are not saved yet.)
* <p/>This method resets those dynamic ID and must be called whenever the actual list of IDs
* change.
*/
public void resetDynamicIds() {
synchronized (mDynamicIds) {
mDynamicIds.clear();
+ mRevDynamicIds.clear();
mDynamicSeed = DYNAMIC_ID_SEED_START;
}
}
- /**
- * Returns a map of (resource name, resource value) for the given {@link ResourceType}.
- * <p/>The values returned are taken from the resource files best matching a given
- * {@link FolderConfiguration}.
- * @param type the type of the resources.
- * @param referenceConfig the configuration to best match.
- */
- private Map<String, ResourceValue> getConfiguredResource(ResourceType type,
- FolderConfiguration referenceConfig) {
- // get the resource item for the given type
- List<ProjectResourceItem> items = mResourceMap.get(type);
-
- // create the map
- HashMap<String, ResourceValue> map = new HashMap<String, ResourceValue>();
-
- for (ProjectResourceItem item : items) {
- // get the source files generating this resource
- List<ResourceFile> list = item.getSourceFileList();
-
- // look for the best match for the given configuration
- Resource match = findMatchingConfiguredResource(list, referenceConfig);
-
- if (match instanceof ResourceFile) {
- ResourceFile matchResFile = (ResourceFile)match;
-
- // get the value of this configured resource.
- ResourceValue value = matchResFile.getValue(type, item.getName());
-
- if (value != null) {
- map.put(item.getName(), value);
- }
- }
- }
-
- return map;
- }
-
- /**
- * Returns the best matching {@link Resource}.
- * @param resources the list of {@link Resource} to choose from.
- * @param referenceConfig the {@link FolderConfiguration} to match.
- * @see http://d.android.com/guide/topics/resources/resources-i18n.html#best-match
- */
- private Resource findMatchingConfiguredResource(List<? extends Resource> resources,
- FolderConfiguration referenceConfig) {
- //
- // 1: eliminate resources that contradict the reference configuration
- // 2: pick next qualifier type
- // 3: check if any resources use this qualifier, if no, back to 2, else move on to 4.
- // 4: eliminate resources that don't use this qualifier.
- // 5: if more than one resource left, go back to 2.
- //
- // The precedence of the qualifiers is more important than the number of qualifiers that
- // exactly match the device.
-
- // 1: eliminate resources that contradict
- ArrayList<Resource> matchingResources = new ArrayList<Resource>();
- for (int i = 0 ; i < resources.size(); i++) {
- Resource res = resources.get(i);
-
- if (res.getConfiguration().isMatchFor(referenceConfig)) {
- matchingResources.add(res);
- }
- }
-
- // if there is only one match, just take it
- if (matchingResources.size() == 1) {
- return matchingResources.get(0);
- } else if (matchingResources.size() == 0) {
- return null;
- }
-
- // 2. Loop on the qualifiers, and eliminate matches
- final int count = FolderConfiguration.getQualifierCount();
- for (int q = 0 ; q < count ; q++) {
- // look to see if one resource has this qualifier.
- // At the same time also record the best match value for the qualifier (if applicable).
-
- // The reference value, to find the best match.
- // Note that this qualifier could be null. In which case any qualifier found in the
- // possible match, will all be considered best match.
- ResourceQualifier referenceQualifier = referenceConfig.getQualifier(q);
-
- boolean found = false;
- ResourceQualifier bestMatch = null; // this is to store the best match.
- for (Resource res : matchingResources) {
- ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
- if (qualifier != null) {
- // set the flag.
- found = true;
-
- // Now check for a best match. If the reference qualifier is null ,
- // any qualifier is a "best" match (we don't need to record all of them.
- // Instead the non compatible ones are removed below)
- if (referenceQualifier != null) {
- if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) {
- bestMatch = qualifier;
- }
- }
- }
- }
-
- // 4. If a resources has a qualifier at the current index, remove all the resources that
- // do not have one, or whose qualifier value does not equal the best match found above
- // unless there's no reference qualifier, in which case they are all considered
- // "best" match.
- if (found) {
- for (int i = 0 ; i < matchingResources.size(); ) {
- Resource res = matchingResources.get(i);
- ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
-
- if (qualifier == null) {
- // this resources has no qualifier of this type: rejected.
- matchingResources.remove(res);
- } else if (referenceQualifier != null && bestMatch != null &&
- bestMatch.equals(qualifier) == false) {
- // there's a reference qualifier and there is a better match for it than
- // this resource, so we reject it.
- matchingResources.remove(res);
- } else {
- // looks like we keep this resource, move on to the next one.
- i++;
- }
- }
-
- // at this point we may have run out of matching resources before going
- // through all the qualifiers.
- if (matchingResources.size() < 2) {
- break;
- }
- }
- }
-
- // Because we accept resources whose configuration have qualifiers where the reference
- // configuration doesn't, we can end up with more than one match. In this case, we just
- // take the first one.
- if (matchingResources.size() == 0) {
- return null;
- }
- return matchingResources.get(0);
- }
-
- /**
- * Checks if the list of {@link ResourceItem}s for the specified {@link ResourceType} needs
- * to be updated.
- * @param type the Resource Type.
- */
- private void checkAndUpdate(ResourceType type) {
- // get the list of folder that can output this type
- ResourceFolderType[] folderTypes = FolderTypeRelationship.getRelatedFolders(type);
-
- for (ResourceFolderType folderType : folderTypes) {
- List<ResourceFolder> folders = mFolderMap.get(folderType);
-
- if (folders != null) {
- for (ResourceFolder folder : folders) {
- if (folder.isTouched()) {
- // if this folder is touched we need to update all the types that can
- // be generated from a file in this folder.
- // This will include 'type' obviously.
- ResourceType[] resTypes = FolderTypeRelationship.getRelatedResourceTypes(
- folderType);
- for (ResourceType resType : resTypes) {
- update(resType);
- }
- return;
- }
- }
- }
- }
+ @Override
+ protected ResourceItem createResourceItem(String name) {
+ return new ResourceItem(name);
}
/**
- * Updates the list of {@link ResourceItem} objects associated with a {@link ResourceType}.
- * This will reset the touch status of all the folders that can generate this resource type.
- * @param type the Resource Type.
+ * Returns a dynamic integer for the given resource name, creating it if it doesn't
+ * already exist.
+ *
+ * @param name the name of the resource
+ * @return an integer.
+ *
+ * @see #resetDynamicIds()
*/
- private void update(ResourceType type) {
- // get the cache list, and lets make a backup
- List<ProjectResourceItem> items = mResourceMap.get(type);
- List<ProjectResourceItem> backup = new ArrayList<ProjectResourceItem>();
-
- if (items == null) {
- items = new ArrayList<ProjectResourceItem>();
- mResourceMap.put(type, items);
- } else {
- // backup the list
- backup.addAll(items);
-
- // we reset the list itself.
- items.clear();
- }
-
- // get the list of folder that can output this type
- ResourceFolderType[] folderTypes = FolderTypeRelationship.getRelatedFolders(type);
-
- for (ResourceFolderType folderType : folderTypes) {
- List<ResourceFolder> folders = mFolderMap.get(folderType);
-
- if (folders != null) {
- for (ResourceFolder folder : folders) {
- items.addAll(folder.getResources(type, this));
- folder.resetTouch();
- }
- }
- }
-
- // now items contains the new list. We "merge" it with the backup list.
- // Basically, we need to keep the old instances of ResourceItem (where applicable),
- // but replace them by the content of the new items.
- // This will let the resource explorer keep the expanded state of the nodes whose data
- // is a ResourceItem object.
- if (backup.size() > 0) {
- // this is not going to change as we're only replacing instances.
- int count = items.size();
-
- for (int i = 0 ; i < count;) {
- // get the "new" item
- ProjectResourceItem item = items.get(i);
-
- // look for a similar item in the old list.
- ProjectResourceItem foundOldItem = null;
- for (ProjectResourceItem oldItem : backup) {
- if (oldItem.getName().equals(item.getName())) {
- foundOldItem = oldItem;
- break;
- }
- }
-
- if (foundOldItem != null) {
- // erase the data of the old item with the data from the new one.
- foundOldItem.replaceWith(item);
-
- // remove the old and new item from their respective lists
- items.remove(i);
- backup.remove(foundOldItem);
-
- // add the old item to the new list
- items.add(foundOldItem);
- } else {
- // this is a new item, we skip to the next object
- i++;
- }
- }
- }
-
- // if this is the ResourceType.ID, we create the actual list, from this list and
- // the compiled resource list.
- if (type == ResourceType.ID) {
- mergeIdResources();
- } else {
- // else this is the list that will actually be displayed, so we sort it.
- Collections.sort(items);
- }
- }
-
private Integer getDynamicId(String name) {
synchronized (mDynamicIds) {
Integer value = mDynamicIds.get(name);
if (value == null) {
- value = new Integer(++mDynamicSeed);
+ value = Integer.valueOf(++mDynamicSeed);
mDynamicIds.put(name, value);
+ mRevDynamicIds.put(value, name);
}
return value;
@@ -898,30 +275,14 @@ public class ProjectResources implements IResourceRepository {
}
/**
- * Looks up an existing {@link ProjectResourceItem} by {@link ResourceType} and name.
- * @param type the Resource Type.
- * @param name the Resource name.
- * @return the existing ResourceItem or null if no match was found.
- */
- protected ProjectResourceItem findResourceItem(ResourceType type, String name) {
- List<ProjectResourceItem> list = mResourceMap.get(type);
-
- for (ProjectResourceItem item : list) {
- if (name.equals(item.getName())) {
- return item;
- }
- }
-
- return null;
- }
-
- /**
* Sets compiled resource information.
+ *
* @param resIdValueToNameMap a map of compiled resource id to resource name.
- * The map is acquired by the {@link ProjectResources} object.
- * @param styleableValueMap
+ * The map is acquired by the {@link ProjectResources} object.
+ * @param styleableValueMap a map of (int[], name) for the styleable information. The map is
+ * acquired by the {@link ProjectResources} object.
* @param resourceValueMap a map of (name, id) for resources of type {@link ResourceType#ID}.
- * The list is acquired by the {@link ProjectResources} object.
+ * The list is acquired by the {@link ProjectResources} object.
*/
void setCompiledResources(Map<Integer, Pair<ResourceType, String>> resIdValueToNameMap,
Map<IntArrayWrapper, String> styleableValueMap,
@@ -932,77 +293,71 @@ public class ProjectResources implements IResourceRepository {
mergeIdResources();
}
+ @Override
+ protected void postUpdate() {
+ super.postUpdate();
+ mergeIdResources();
+ }
+
/**
* Merges the list of ID resource coming from R.java and the list of ID resources
* coming from XML declaration into the cached list {@link #mIdResourceList}.
*/
void mergeIdResources() {
- // get the list of IDs coming from XML declaration. Those ids are present in
- // mCompiledIdResources already, so we'll need to use those instead of creating
- // new IdResourceItem
- List<ProjectResourceItem> xmlIdResources = mResourceMap.get(ResourceType.ID);
-
- synchronized (mIdResourceList) {
- // copy the currently cached items.
- ArrayList<IdResourceItem> oldItems = new ArrayList<IdResourceItem>();
- oldItems.addAll(mIdResourceList);
-
- // empty the current list
- mIdResourceList.clear();
-
- // get the list of compile id resources.
- Map<String, Integer> idMap = null;
- if (mResourceValueMap != null) {
- idMap = mResourceValueMap.get(ResourceType.ID);
- }
+ if (mResourceValueMap == null) {
+ return;
+ }
- if (idMap == null) {
- if (xmlIdResources != null) {
- for (ProjectResourceItem resourceItem : xmlIdResources) {
- // check the actual class just for safety.
- if (resourceItem instanceof IdResourceItem) {
- mIdResourceList.add((IdResourceItem)resourceItem);
- }
- }
- }
- } else {
- // loop on the full list of id, and look for a match in the old list,
- // in the list coming from XML (in case a new XML item was created.)
-
- Set<String> idSet = idMap.keySet();
-
- idLoop: for (String idResource : idSet) {
- // first look in the XML list in case an id went from inline to XML declared.
- if (xmlIdResources != null) {
- for (ProjectResourceItem resourceItem : xmlIdResources) {
- if (resourceItem instanceof IdResourceItem &&
- resourceItem.getName().equals(idResource)) {
- mIdResourceList.add((IdResourceItem)resourceItem);
- continue idLoop;
- }
- }
- }
+ // get the current ID values
+ List<ResourceItem> resources = mResourceMap.get(ResourceType.ID);
+
+ // get the ID values coming from the R class.
+ Map<String, Integer> rResources = mResourceValueMap.get(ResourceType.ID);
- // if we haven't found it, look in the old items.
- int count = oldItems.size();
- for (int i = 0 ; i < count ; i++) {
- IdResourceItem resourceItem = oldItems.get(i);
- if (resourceItem.getName().equals(idResource)) {
- oldItems.remove(i);
- mIdResourceList.add(resourceItem);
- continue idLoop;
+ if (rResources != null) {
+ Map<String, Integer> copy;
+
+ if (resources == null) {
+ resources = new ArrayList<ResourceItem>(rResources.entrySet().size());
+ mResourceMap.put(ResourceType.ID, resources);
+ copy = rResources;
+ } else {
+ // make a copy of the compiled Resources.
+ // As we loop on the full resources, we'll check with this copy map and remove
+ // from it all the resources we find in the full list.
+ // At the end, whatever is in the copy of the compile list is not in the full map,
+ // and should be added as inlined resource items.
+ copy = new HashMap<String, Integer>(rResources);
+
+ for (int i = 0 ; i < resources.size(); ) {
+ ResourceItem item = resources.get(i);
+ String name = item.getName();
+ if (item.isDeclaredInline()) {
+ // This ID is declared inline in the full resource map.
+ // Check if it's also in the compiled version, in which case we can keep it.
+ // Otherwise, if it doesn't exist in the compiled map, remove it from the
+ // full map.
+ // Since we're going to remove it from the copy map either way, we can use
+ // remove to test if it's there
+ if (copy.remove(name) != null) {
+ // there is a match in the compiled list, do nothing, keep current one.
+ i++;
+ } else {
+ // the ID is now gone, remove it from the list
+ resources.remove(i);
}
+ } else {
+ // not an inline item, remove it from the copy.
+ copy.remove(name);
+ i++;
}
-
- // if we haven't found it, it looks like it's a new id that was
- // declared inline.
- mIdResourceList.add(new IdResourceItem(idResource,
- true /* isDeclaredInline */));
}
}
- // now we sort the list
- Collections.sort(mIdResourceList);
+ // now add what's left in copy to the list
+ for (String name : copy.keySet()) {
+ resources.add(new InlineResourceItem(name));
+ }
}
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Resource.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Resource.java
deleted file mode 100644
index fd9005b..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Resource.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.internal.resources.manager;
-
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-
-/**
- * Base class for file system resource items (Folders, Files).
- */
-public abstract class Resource {
- private boolean mTouched = true;
-
- /**
- * Returns the {@link FolderConfiguration} for this object.
- */
- public abstract FolderConfiguration getConfiguration();
-
- /**
- * Indicates that the underlying file was changed.
- */
- public final void touch() {
- mTouched = true;
- }
-
- public final boolean isTouched() {
- return mTouched;
- }
-
- public final void resetTouch() {
- mTouched = false;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
index 684620b..e41cde5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
@@ -16,22 +16,28 @@
package com.android.ide.eclipse.adt.internal.resources.manager;
+import com.android.annotations.VisibleForTesting;
+import com.android.annotations.VisibleForTesting.Visibility;
+import com.android.ide.common.resources.FrameworkResources;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceFolder;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFolderListener;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IProjectListener;
+import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IResourceEventListener;
import com.android.ide.eclipse.adt.io.IFileWrapper;
import com.android.ide.eclipse.adt.io.IFolderWrapper;
-import com.android.resources.ResourceType;
+import com.android.io.FolderWrapper;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.io.IAbstractResource;
+import com.android.resources.ResourceFolderType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.FolderWrapper;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.IAbstractFolder;
-import com.android.sdklib.io.IAbstractResource;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -47,6 +53,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* The ResourceManager tracks resources for all opened projects.
@@ -66,12 +73,10 @@ import java.util.HashMap;
* @see ProjectResources
*/
public final class ResourceManager {
+ public final static boolean DEBUG = false;
private final static ResourceManager sThis = new ResourceManager();
- /** List of the qualifier object helping for the parsing of folder names */
- private final ResourceQualifier[] mQualifiers;
-
/**
* Map associating project resource with project objects.
* <p/><b>All accesses must be inside a synchronized(mMap) block</b>, and do as a little as
@@ -110,7 +115,9 @@ public final class ResourceManager {
* @param monitor The global project monitor
*/
public static void setup(GlobalProjectMonitor monitor) {
+ monitor.addResourceEventListener(sThis.mResourceEventListener);
monitor.addProjectListener(sThis.mProjectListener);
+
int mask = IResourceDelta.ADDED | IResourceDelta.REMOVED | IResourceDelta.CHANGED;
monitor.addFolderListener(sThis.mFolderListener, mask);
monitor.addFileListener(sThis.mFileListener, mask);
@@ -156,6 +163,40 @@ public final class ResourceManager {
}
}
+ private class ResourceEventListener implements IResourceEventListener {
+ private final List<IProject> mChangedProjects = new ArrayList<IProject>();
+
+ public void resourceChangeEventEnd() {
+ for (IProject project : mChangedProjects) {
+ ProjectResources resources;
+ synchronized (mMap) {
+ resources = mMap.get(project);
+ }
+
+ resources.postUpdate();
+ }
+
+ mChangedProjects.clear();
+ }
+
+ public void resourceChangeEventStart() {
+ // pass
+ }
+
+ void addProject(IProject project) {
+ if (mChangedProjects.contains(project) == false) {
+ mChangedProjects.add(project);
+ }
+ }
+ }
+
+ /**
+ * Delegate listener for resource changes. This is called before and after any calls to the
+ * project and file listeners (for a given resource change event).
+ */
+ private ResourceEventListener mResourceEventListener = new ResourceEventListener();
+
+
/**
* Implementation of the {@link IFolderListener} as an internal class so that the methods
* do not appear in the public API of {@link ResourceManager}.
@@ -167,7 +208,7 @@ public final class ResourceManager {
final IProject project = folder.getProject();
try {
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
return;
}
} catch (CoreException e) {
@@ -175,6 +216,8 @@ public final class ResourceManager {
return;
}
+ mResourceEventListener.addProject(project);
+
switch (kind) {
case IResourceDelta.ADDED:
// checks if the folder is under res.
@@ -194,8 +237,8 @@ public final class ResourceManager {
}
}
- ResourceFolder newFolder = processFolder(new IFolderWrapper(folder),
- resources);
+ ResourceFolder newFolder = resources.processFolder(
+ new IFolderWrapper(folder));
if (newFolder != null) {
notifyListenerOnFolderChange(project, newFolder, kind);
}
@@ -203,13 +246,13 @@ public final class ResourceManager {
}
break;
case IResourceDelta.CHANGED:
+ // only call the listeners.
synchronized (mMap) {
resources = mMap.get(folder.getProject());
}
if (resources != null) {
ResourceFolder resFolder = resources.getResourceFolder(folder);
if (resFolder != null) {
- resFolder.touch();
notifyListenerOnFolderChange(project, resFolder, kind);
}
}
@@ -223,7 +266,8 @@ public final class ResourceManager {
ResourceFolderType type = ResourceFolderType.getFolderType(
folder.getName());
- ResourceFolder removedFolder = resources.removeFolder(type, folder);
+ ResourceFolder removedFolder = resources.removeFolder(type,
+ new IFolderWrapper(folder));
if (removedFolder != null) {
notifyListenerOnFolderChange(project, removedFolder, kind);
}
@@ -251,12 +295,10 @@ public final class ResourceManager {
* @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_DEFAULT) == false) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
return;
}
} catch (CoreException e) {
@@ -264,80 +306,38 @@ public final class ResourceManager {
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
- synchronized (mMap) {
- resources = mMap.get(project);
- }
-
- IContainer container = file.getParent();
- if (container instanceof IFolder && resources != null) {
-
- ResourceFolder folder = resources.getResourceFolder(
- (IFolder)container);
-
- if (folder != null) {
- ResourceFile resFile = processFile(
- new IFileWrapper(file), folder);
- notifyListenerOnFileChange(project, resFile, kind);
- }
- }
- }
- }
- break;
- case IResourceDelta.CHANGED:
- // try to find a matching ResourceFile
- synchronized (mMap) {
- resources = mMap.get(project);
- }
- if (resources != null) {
- IContainer container = file.getParent();
- if (container instanceof IFolder) {
- ResourceFolder resFolder = resources.getResourceFolder(
- (IFolder)container);
+ // get the project resources
+ ProjectResources resources;
+ synchronized (mMap) {
+ resources = mMap.get(project);
+ }
- // we get the delete on the folder before the file, so it is possible
- // the associated ResourceFolder doesn't exist anymore.
- if (resFolder != null) {
- // get the resourceFile, and touch it.
- ResourceFile resFile = resFolder.getFile(file);
- if (resFile != null) {
- resFile.touch();
- notifyListenerOnFileChange(project, resFile, kind);
- }
- }
- }
- }
- break;
- case IResourceDelta.REMOVED:
- // try to find a matching ResourceFile
- synchronized (mMap) {
- resources = mMap.get(project);
- }
- if (resources != null) {
- IContainer container = file.getParent();
- if (container instanceof IFolder) {
- ResourceFolder resFolder = resources.getResourceFolder(
- (IFolder)container);
+ if (resources == null) {
+ return;
+ }
- // we get the delete on the folder before the file, so it is possible
- // the associated ResourceFolder doesn't exist anymore.
- if (resFolder != null) {
- // remove the file
- ResourceFile resFile = resFolder.removeFile(file);
- if (resFile != null) {
- notifyListenerOnFileChange(project, resFile, kind);
- }
- }
+ // checks if the file is under res/something.
+ IPath path = file.getFullPath();
+
+ if (path.segmentCount() == 4) {
+ if (isInResFolder(path)) {
+ IContainer container = file.getParent();
+ if (container instanceof IFolder) {
+
+ ResourceFolder folder = resources.getResourceFolder(
+ (IFolder)container);
+
+ // folder can be null as when the whole folder is deleted, the
+ // REMOVED event for the folder comes first. In this case, the
+ // folder will have taken care of things.
+ if (folder != null) {
+ ResourceFile resFile = folder.processFile(
+ new IFileWrapper(file),
+ ResourceHelper.getResourceDeltaKind(kind));
+ notifyListenerOnFileChange(project, resFile, kind);
}
}
- break;
+ }
}
}
};
@@ -409,15 +409,16 @@ public final class ResourceManager {
* 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) {
+ public ResourceRepository loadFrameworkResources(IAndroidTarget androidTarget) {
String osResourcesPath = androidTarget.getPath(IAndroidTarget.RESOURCES);
FolderWrapper frameworkRes = new FolderWrapper(osResourcesPath);
if (frameworkRes.exists()) {
- ProjectResources resources = new ProjectResources();
+ FrameworkResources resources = new FrameworkResources();
try {
loadResources(resources, frameworkRes);
+ resources.loadPublicResources(frameworkRes, AdtPlugin.getDefault());
return resources;
} catch (IOException e) {
// since we test that folders are folders, and files are files, this shouldn't
@@ -429,7 +430,7 @@ public final class ResourceManager {
}
/**
- * Loads the resources from a folder, and fills the given {@link ProjectResources}.
+ * Loads the resources from a folder, and fills the given {@link ResourceRepository}.
* <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
@@ -443,19 +444,20 @@ public final class ResourceManager {
* 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 resources The {@link ResourceRepository} files to fill.
+ * This is filled up with the content of the folder.
* @param rootFolder The folder to read the resources from. This is the top level
* resource folder (res/)
* @throws IOException
*/
- public void loadResources(ProjectResources resources, IAbstractFolder rootFolder)
+ @VisibleForTesting(visibility=Visibility.PRIVATE)
+ public void loadResources(ResourceRepository resources, IAbstractFolder rootFolder)
throws IOException {
IAbstractResource[] files = rootFolder.listMembers();
for (IAbstractResource file : files) {
if (file instanceof IAbstractFolder) {
IAbstractFolder folder = (IAbstractFolder) file;
- ResourceFolder resFolder = processFolder(folder, resources);
+ ResourceFolder resFolder = resources.processFolder(folder);
if (resFolder != null) {
// now we process the content of the folder
@@ -463,15 +465,13 @@ public final class ResourceManager {
for (IAbstractResource childRes : children) {
if (childRes instanceof IAbstractFile) {
- processFile((IAbstractFile) childRes, resFolder);
+ resFolder.processFile((IAbstractFile) childRes,
+ ResourceHelper.getResourceDeltaKind(IResourceDelta.ADDED));
}
}
}
}
}
-
- // now that we have loaded the files, we need to force load the resources from them
- resources.loadAll();
}
/**
@@ -481,7 +481,7 @@ public final class ResourceManager {
private void createProject(IProject project) {
if (project.isOpen()) {
try {
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
return;
}
} catch (CoreException e1) {
@@ -507,8 +507,8 @@ public final class ResourceManager {
for (IResource res : resources) {
if (res.getType() == IResource.FOLDER) {
IFolder folder = (IFolder)res;
- ResourceFolder resFolder = processFolder(new IFolderWrapper(folder),
- projectResources);
+ ResourceFolder resFolder = projectResources.processFolder(
+ new IFolderWrapper(folder));
if (resFolder != null) {
// now we process the content of the folder
@@ -518,12 +518,16 @@ public final class ResourceManager {
if (fileRes.getType() == IResource.FILE) {
IFile file = (IFile)fileRes;
- processFile(new IFileWrapper(file), resFolder);
+ resFolder.processFile(new IFileWrapper(file),
+ ResourceHelper.getResourceDeltaKind(
+ IResourceDelta.ADDED));
}
}
}
}
}
+
+ projectResources.postUpdate();
} catch (CoreException e) {
// This happens if the project is closed or if the folder doesn't exist.
// Since we already test for that, we can ignore this exception.
@@ -532,111 +536,6 @@ public final class ResourceManager {
}
}
- /**
- * Creates a {@link FolderConfiguration} matching the folder segments.
- * @param folderSegments The segments of the folder name. The first segments should contain
- * the name of the folder
- * @return a FolderConfiguration object, or null if the folder name isn't valid..
- */
- public FolderConfiguration getConfig(String[] folderSegments) {
- FolderConfiguration config = new FolderConfiguration();
-
- // we are going to loop through the segments, and match them with the first
- // available qualifier. If the segment doesn't match we try with the next qualifier.
- // 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) {
- while (qualifierIndex < qualifierCount &&
- 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;
- }
- }
-
- return config;
- }
-
- /**
- * Processes a folder and adds it to the list of the project resources.
- * @param folder the folder to process
- * @param project the folder's project.
- * @return the ConfiguredFolder created from this folder, or null if the process failed.
- */
- private ResourceFolder processFolder(IAbstractFolder folder, ProjectResources project) {
- // split the name of the folder in segments.
- String[] folderSegments = folder.getName().split(FolderConfiguration.QUALIFIER_SEP);
-
- // 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;
- }
-
- /**
- * Processes a file and adds it to its parent folder resource.
- * @param file the underlying resource file.
- * @param folder the parent of the resource file.
- * @return the {@link ResourceFile} that was created.
- */
- private ResourceFile 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
- // the ability to have 2 files in the same folder generating 2 different types of
- // 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);
- }
-
- return resFile;
- }
/**
* Returns true if the path is under /project/res/
@@ -678,9 +577,19 @@ public final class ResourceManager {
* Private constructor to enforce singleton design.
*/
private ResourceManager() {
- // get the default qualifiers.
- FolderConfiguration defaultConfig = new FolderConfiguration();
- defaultConfig.createDefault();
- mQualifiers = defaultConfig.getQualifiers();
+ }
+
+ // debug only
+ @SuppressWarnings("unused")
+ private String getKindString(int kind) {
+ if (DEBUG) {
+ switch (kind) {
+ case IResourceDelta.ADDED: return "ADDED";
+ case IResourceDelta.REMOVED: return "REMOVED";
+ case IResourceDelta.CHANGED: return "CHANGED";
+ }
+ }
+
+ return Integer.toString(kind);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidJarLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidJarLoader.java
index 3bb125a..807acfc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidJarLoader.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidJarLoader.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.sdk;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
@@ -155,7 +155,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidClassLoader
// get the name of the entry.
String entryPath = entry.getName();
- if (!entryPath.endsWith(AndroidConstants.DOT_CLASS)) {
+ if (!entryPath.endsWith(AdtConstants.DOT_CLASS)) {
// only accept class files
continue;
}
@@ -219,7 +219,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidClassLoader
while ((entry = zis.getNextEntry()) != null) {
// get the name of the entry and convert to a class binary name
String entryPath = entry.getName();
- if (!entryPath.endsWith(AndroidConstants.DOT_CLASS)) {
+ if (!entryPath.endsWith(AdtConstants.DOT_CLASS)) {
// only accept class files
continue;
}
@@ -341,7 +341,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidClassLoader
// The name is a binary name. Something like "android.R", or "android.R$id".
// Make a path out of it.
- String entryName = className.replaceAll("\\.", "/") + AndroidConstants.DOT_CLASS; //$NON-NLS-1$ //$NON-NLS-2$
+ String entryName = className.replaceAll("\\.", "/") + AdtConstants.DOT_CLASS; //$NON-NLS-1$ //$NON-NLS-2$
// create streams to read the intermediary archive
FileInputStream fis = new FileInputStream(mOsFrameworkLocation);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
index ae81ed6..69f1267 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
@@ -18,15 +18,19 @@ package com.android.ide.eclipse.adt.internal.sdk;
import com.android.ide.common.rendering.LayoutLibrary;
import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.common.sdk.LoadStatus;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.animator.AnimDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.color.ColorDescriptors;
import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableDescriptors;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.adt.internal.editors.menu.descriptors.MenuDescriptors;
import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors;
import com.android.ide.eclipse.adt.internal.editors.xml.descriptors.XmlDescriptors;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
@@ -51,6 +55,10 @@ public class AndroidTargetData {
public final static int DESCRIPTOR_SEARCHABLE = 6;
public final static int DESCRIPTOR_PREFERENCES = 7;
public final static int DESCRIPTOR_APPWIDGET_PROVIDER = 8;
+ public final static int DESCRIPTOR_DRAWABLE = 9;
+ public final static int DESCRIPTOR_ANIMATOR = 10;
+ public final static int DESCRIPTOR_ANIM = 11;
+ public final static int DESCRIPTOR_COLOR = 12;
private final IAndroidTarget mTarget;
@@ -66,16 +74,18 @@ public class AndroidTargetData {
*/
private Hashtable<String, String[]> mAttributeValues = new Hashtable<String, String[]>();
- private IResourceRepository mSystemResourceRepository;
-
private AndroidManifestDescriptors mManifestDescriptors;
+ private DrawableDescriptors mDrawableDescriptors;
+ private AnimatorDescriptors mAnimatorDescriptors;
+ private AnimDescriptors mAnimDescriptors;
+ private ColorDescriptors mColorDescriptors;
private LayoutDescriptors mLayoutDescriptors;
private MenuDescriptors mMenuDescriptors;
private XmlDescriptors mXmlDescriptors;
private Map<String, Map<String, Integer>> mEnumValueMap;
- private ProjectResources mFrameworkResources;
+ private ResourceRepository mFrameworkResources;
private LayoutLibrary mLayoutLibrary;
private boolean mLayoutBridgeInit = false;
@@ -86,14 +96,16 @@ public class AndroidTargetData {
/**
* Creates an AndroidTargetData object.
- * @param platformLibraries
- * @param optionalLibraries
*/
- void setExtraData(IResourceRepository systemResourceRepository,
+ void setExtraData(
AndroidManifestDescriptors manifestDescriptors,
LayoutDescriptors layoutDescriptors,
MenuDescriptors menuDescriptors,
XmlDescriptors xmlDescriptors,
+ DrawableDescriptors drawableDescriptors,
+ AnimatorDescriptors animatorDescriptors,
+ AnimDescriptors animDescriptors,
+ ColorDescriptors colorDescriptors,
Map<String, Map<String, Integer>> enumValueMap,
String[] permissionValues,
String[] activityIntentActionValues,
@@ -102,16 +114,19 @@ public class AndroidTargetData {
String[] intentCategoryValues,
String[] platformLibraries,
IOptionalLibrary[] optionalLibraries,
- ProjectResources resources,
+ ResourceRepository frameworkResources,
LayoutLibrary layoutLibrary) {
- mSystemResourceRepository = systemResourceRepository;
mManifestDescriptors = manifestDescriptors;
+ mDrawableDescriptors = drawableDescriptors;
+ mAnimatorDescriptors = animatorDescriptors;
+ mAnimDescriptors = animDescriptors;
+ mColorDescriptors = colorDescriptors;
mLayoutDescriptors = layoutDescriptors;
mMenuDescriptors = menuDescriptors;
mXmlDescriptors = xmlDescriptors;
mEnumValueMap = enumValueMap;
- mFrameworkResources = resources;
+ mFrameworkResources = frameworkResources;
mLayoutLibrary = layoutLibrary;
setPermissions(permissionValues);
@@ -120,10 +135,6 @@ public class AndroidTargetData {
setOptionalLibraries(platformLibraries, optionalLibraries);
}
- public IResourceRepository getSystemResources() {
- return mSystemResourceRepository;
- }
-
/**
* Returns an {@link IDescriptorProvider} from a given Id.
* The Id can be one of {@link #DESCRIPTOR_MANIFEST}, {@link #DESCRIPTOR_LAYOUT},
@@ -149,6 +160,14 @@ public class AndroidTargetData {
return mXmlDescriptors.getAppWidgetProvider();
case DESCRIPTOR_SEARCHABLE:
return mXmlDescriptors.getSearchableProvider();
+ case DESCRIPTOR_DRAWABLE:
+ return mDrawableDescriptors;
+ case DESCRIPTOR_ANIMATOR:
+ return mAnimatorDescriptors;
+ case DESCRIPTOR_ANIM:
+ return mAnimDescriptors;
+ case DESCRIPTOR_COLOR:
+ return mColorDescriptors;
default :
throw new IllegalArgumentException();
}
@@ -162,6 +181,34 @@ public class AndroidTargetData {
}
/**
+ * Returns the drawable descriptors
+ */
+ public DrawableDescriptors getDrawableDescriptors() {
+ return mDrawableDescriptors;
+ }
+
+ /**
+ * Returns the animation descriptors
+ */
+ public AnimDescriptors getAnimDescriptors() {
+ return mAnimDescriptors;
+ }
+
+ /**
+ * Returns the color descriptors
+ */
+ public ColorDescriptors getColorDescriptors() {
+ return mColorDescriptors;
+ }
+
+ /**
+ * Returns the animator descriptors
+ */
+ public AnimatorDescriptors getAnimatorDescriptors() {
+ return mAnimatorDescriptors;
+ }
+
+ /**
* Returns the layout Descriptors.
*/
public LayoutDescriptors getLayoutDescriptors() {
@@ -239,7 +286,7 @@ public class AndroidTargetData {
/**
* Returns the {@link ProjectResources} containing the Framework Resources.
*/
- public ProjectResources getFrameworkResources() {
+ public ResourceRepository getFrameworkResources() {
return mFrameworkResources;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
index 6426fdb..19b2e3d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
@@ -17,19 +17,20 @@
package com.android.ide.eclipse.adt.internal.sdk;
import com.android.ide.common.rendering.LayoutLibrary;
+import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.common.resources.platform.AttrsXmlParser;
import com.android.ide.common.resources.platform.DeclareStyleableInfo;
import com.android.ide.common.resources.platform.ViewClassInfo;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.animator.AnimDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.color.ColorDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableDescriptors;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.adt.internal.editors.menu.descriptors.MenuDescriptors;
import com.android.ide.eclipse.adt.internal.editors.xml.descriptors.XmlDescriptors;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-import com.android.resources.ResourceType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
@@ -86,7 +87,7 @@ public final class AndroidTargetParser {
try {
SubMonitor progress = SubMonitor.convert(monitor,
String.format("Parsing SDK %1$s", mAndroidTarget.getName()),
- 13);
+ 16);
AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget);
@@ -101,15 +102,6 @@ public final class AndroidTargetParser {
return Status.CANCEL_STATUS;
}
- // get the resource Ids.
- progress.subTask("Resource IDs");
- IResourceRepository frameworkRepository = collectResourceIds(classLoader);
- progress.worked(1);
-
- if (progress.isCanceled()) {
- return Status.CANCEL_STATUS;
- }
-
// get the permissions
progress.subTask("Permissions");
String[] permissionValues = collectPermissions(classLoader);
@@ -230,9 +222,42 @@ public final class AndroidTargetParser {
preferenceGroupsInfo);
progress.worked(1);
+ if (progress.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+
+ DrawableDescriptors drawableDescriptors = new DrawableDescriptors();
+ Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList();
+ drawableDescriptors.updateDescriptors(map);
+ progress.worked(1);
+
+ if (progress.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+
+ AnimatorDescriptors animatorDescriptors = new AnimatorDescriptors();
+ animatorDescriptors.updateDescriptors(map);
+ progress.worked(1);
+
+ if (progress.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+
+ AnimDescriptors animDescriptors = new AnimDescriptors();
+ animDescriptors.updateDescriptors(map);
+ progress.worked(1);
+
+ if (progress.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+
+ ColorDescriptors colorDescriptors = new ColorDescriptors();
+ colorDescriptors.updateDescriptors(map);
+ progress.worked(1);
+
// load the framework resources.
- ProjectResources resources = ResourceManager.getInstance().loadFrameworkResources(
- mAndroidTarget);
+ ResourceRepository frameworkResources =
+ ResourceManager.getInstance().loadFrameworkResources(mAndroidTarget);
progress.worked(1);
// now load the layout lib bridge
@@ -243,11 +268,15 @@ public final class AndroidTargetParser {
progress.worked(1);
// and finally create the PlatformData with all that we loaded.
- targetData.setExtraData(frameworkRepository,
+ targetData.setExtraData(
manifestDescriptors,
layoutDescriptors,
menuDescriptors,
xmlDescriptors,
+ drawableDescriptors,
+ animatorDescriptors,
+ animDescriptors,
+ colorDescriptors,
enumValueMap,
permissionValues,
activity_actions.toArray(new String[activity_actions.size()]),
@@ -256,7 +285,7 @@ public final class AndroidTargetParser {
categories.toArray(new String[categories.size()]),
mAndroidTarget.getPlatformLibraries(),
mAndroidTarget.getOptionalLibraries(),
- resources,
+ frameworkResources,
layoutBridge);
Sdk.getCurrent().setTargetData(mAndroidTarget, targetData);
@@ -290,72 +319,6 @@ public final class AndroidTargetParser {
}
/**
- * Creates an IResourceRepository for the framework resources.
- *
- * @param classLoader The framework SDK jar classloader
- * @return a map of the resources, or null if it failed.
- */
- private IResourceRepository collectResourceIds(
- AndroidJarLoader classLoader) {
- try {
- Class<?> r = classLoader.loadClass(SdkConstants.CLASS_R);
-
- if (r != null) {
- Map<ResourceType, List<ResourceItem>> map = parseRClass(r);
- if (map != null) {
- return new FrameworkResourceRepository(map);
- }
- }
- } catch (ClassNotFoundException e) {
- AdtPlugin.logAndPrintError(e, TAG,
- "Collect resource IDs failed, class %1$s not found in %2$s", //$NON-NLS-1$
- SdkConstants.CLASS_R,
- mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR));
- }
-
- return null;
- }
-
- /**
- * Parse the R class and build the resource map.
- *
- * @param rClass the Class object representing the Resources.
- * @return a map of the resource or null
- */
- private Map<ResourceType, List<ResourceItem>> parseRClass(Class<?> rClass) {
- // get the sub classes.
- Class<?>[] classes = rClass.getClasses();
-
- if (classes.length > 0) {
- HashMap<ResourceType, List<ResourceItem>> map =
- new HashMap<ResourceType, List<ResourceItem>>();
-
- // get the fields of each class.
- for (int c = 0 ; c < classes.length ; c++) {
- Class<?> subClass = classes[c];
- String name = subClass.getSimpleName();
-
- // get the matching ResourceType
- ResourceType type = ResourceType.getEnum(name);
- if (type != null) {
- List<ResourceItem> list = new ArrayList<ResourceItem>();
- map.put(type, list);
-
- Field[] fields = subClass.getFields();
-
- for (Field f : fields) {
- list.add(new ResourceItem(f.getName()));
- }
- }
- }
-
- return map;
- }
-
- return null;
- }
-
- /**
* Loads, collects and returns the list of default permissions from the framework.
*
* @param classLoader The framework SDK jar classloader
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/FrameworkResourceRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/FrameworkResourceRepository.java
deleted file mode 100644
index 247a888..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/FrameworkResourceRepository.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.internal.sdk;
-
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.resources.ResourceType;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Implementation of the {@link IResourceRepository} interface to hold the system resource Ids
- * parsed by {@link AndroidTargetParser}.
- */
-final class FrameworkResourceRepository implements IResourceRepository {
-
- private Map<ResourceType, List<ResourceItem>> mResourcesMap;
-
- public FrameworkResourceRepository(Map<ResourceType, List<ResourceItem>> systemResourcesMap) {
- mResourcesMap = systemResourcesMap;
- }
-
- public ResourceType[] getAvailableResourceTypes() {
- if (mResourcesMap != null) {
- Set<ResourceType> types = mResourcesMap.keySet();
-
- if (types != null) {
- return types.toArray(new ResourceType[types.size()]);
- }
- }
-
- return null;
- }
-
- public ResourceItem[] getResources(ResourceType type) {
- if (mResourcesMap != null) {
- List<ResourceItem> items = mResourcesMap.get(type);
-
- if (items != null) {
- return items.toArray(new ResourceItem[items.size()]);
- }
- }
-
- return null;
- }
-
- public boolean hasResources(ResourceType type) {
- if (mResourcesMap != null) {
- List<ResourceItem> items = mResourcesMap.get(type);
-
- return (items != null && items.size() > 0);
- }
-
- return false;
- }
-
- public boolean isSystemRepository() {
- return true;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java
index 4f7b7f2..60b528a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java
@@ -16,19 +16,19 @@
package com.android.ide.eclipse.adt.internal.sdk;
-import com.android.ide.eclipse.adt.internal.resources.configurations.CountryCodeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationStateQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NetworkCodeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
-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.common.resources.configuration.CountryCodeQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
+import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
+import com.android.ide.common.resources.configuration.NavigationStateQualifier;
+import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
+import com.android.ide.common.resources.configuration.PixelDensityQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
+import com.android.ide.common.resources.configuration.TouchScreenQualifier;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceHandler.java
index 12c58e8..3ad4e61 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceHandler.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceHandler.java
@@ -16,19 +16,19 @@
package com.android.ide.eclipse.adt.internal.sdk;
-import com.android.ide.eclipse.adt.internal.resources.configurations.CountryCodeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationStateQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NetworkCodeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
-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.common.resources.configuration.CountryCodeQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
+import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
+import com.android.ide.common.resources.configuration.NavigationStateQualifier;
+import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
+import com.android.ide.common.resources.configuration.PixelDensityQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
+import com.android.ide.common.resources.configuration.TouchScreenQualifier;
import com.android.resources.Density;
import com.android.resources.Keyboard;
import com.android.resources.KeyboardState;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java
index 60c8d95..57d1e50 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.sdk;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice.DeviceConfig;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
index 5d925db..8d3ee7b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
@@ -20,7 +20,7 @@ import com.android.ddmlib.IDevice;
import com.android.ide.common.rendering.LayoutLibrary;
import com.android.ide.common.sdk.LoadStatus;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.build.DexWrapper;
import com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
@@ -31,6 +31,7 @@ import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonit
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IResourceEventListener;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryDifference;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryState;
+import com.android.io.StreamException;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
@@ -41,7 +42,6 @@ import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
-import com.android.sdklib.io.StreamException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
@@ -743,7 +743,7 @@ public final class Sdk {
private void onProjectRemoved(IProject project, boolean deleted) {
try {
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
return;
}
} catch (CoreException e) {
@@ -830,7 +830,7 @@ public final class Sdk {
private void onProjectOpened(final IProject openedProject) {
try {
- if (openedProject.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (openedProject.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
return;
}
} catch (CoreException e) {
@@ -897,7 +897,7 @@ public final class Sdk {
public void projectRenamed(IProject project, IPath from) {
try {
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
return;
}
} catch (CoreException e) {
@@ -964,7 +964,7 @@ public final class Sdk {
// the target.
IProject iProject = file.getProject();
- if (iProject.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (iProject.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
return;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sourcelookup/AdtSourceLookupDirector.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sourcelookup/AdtSourceLookupDirector.java
index 612ef76..e0e236b 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sourcelookup/AdtSourceLookupDirector.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sourcelookup/AdtSourceLookupDirector.java
@@ -17,7 +17,9 @@
package com.android.ide.eclipse.adt.internal.sourcelookup;
-import com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
@@ -32,6 +34,7 @@ import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.launching.JavaSourceLookupDirector;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import java.io.File;
@@ -41,20 +44,35 @@ public class AdtSourceLookupDirector extends JavaSourceLookupDirector {
public void initializeDefaults(ILaunchConfiguration configuration) throws CoreException {
dispose();
setLaunchConfiguration(configuration);
- String projectName = configuration.getAttribute("org.eclipse.jdt.launching.PROJECT_ATTR", //$NON-NLS-1$
+ String projectName =
+ configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
""); //$NON-NLS-1$
if (projectName != null && projectName.length() > 0) {
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
if (project != null && project.isOpen()) {
+ ProjectState state = Sdk.getProjectState(project);
+ if (state == null) {
+ initDefaults();
+ return;
+ }
+ IAndroidTarget target = state.getTarget();
+ if (target == null) {
+ initDefaults();
+ return;
+ }
+ String path = target.getPath(IAndroidTarget.ANDROID_JAR);
+ if (path == null) {
+ initDefaults();
+ return;
+ }
IJavaProject javaProject = JavaCore.create(project);
if (javaProject != null && javaProject.isOpen()) {
- IClasspathEntry[] entries = javaProject.getRawClasspath();
+ IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
IClasspathEntry androidEntry = null;
for (int i = 0; i < entries.length; i++) {
IClasspathEntry entry = entries[i];
- if (entry.getPath() != null
- && AndroidClasspathContainerInitializer.CONTAINER_ID.equals(entry
- .getPath().toString())) {
+ if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY
+ && path.equals(entry.getPath().toString())) {
androidEntry = entry;
break;
}
@@ -88,6 +106,10 @@ public class AdtSourceLookupDirector extends JavaSourceLookupDirector {
}
}
}
+ initDefaults();
+ }
+
+ private void initDefaults() {
setSourceContainers(new ISourceContainer[] {
new DefaultSourceContainer()
});
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java
index e8ca524..74120fd 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java
@@ -16,26 +16,27 @@
package com.android.ide.eclipse.adt.internal.ui;
-import com.android.ide.eclipse.adt.internal.resources.configurations.CountryCodeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.DockModeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationStateQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NetworkCodeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NightModeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
-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.manager.ResourceManager;
+import com.android.AndroidConstants;
+import com.android.ide.common.resources.configuration.CountryCodeQualifier;
+import com.android.ide.common.resources.configuration.DockModeQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
+import com.android.ide.common.resources.configuration.LanguageQualifier;
+import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
+import com.android.ide.common.resources.configuration.NavigationStateQualifier;
+import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
+import com.android.ide.common.resources.configuration.NightModeQualifier;
+import com.android.ide.common.resources.configuration.PixelDensityQualifier;
+import com.android.ide.common.resources.configuration.RegionQualifier;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
+import com.android.ide.common.resources.configuration.TouchScreenQualifier;
+import com.android.ide.common.resources.configuration.VersionQualifier;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
import com.android.resources.Density;
import com.android.resources.DockMode;
import com.android.resources.Keyboard;
@@ -490,7 +491,7 @@ public class ConfigurationSelector extends Composite {
* @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);
+ FolderConfiguration config = FolderConfiguration.getConfig(folderSegments);
if (config == null) {
return false;
@@ -509,7 +510,7 @@ public class ConfigurationSelector extends Composite {
*/
public boolean setConfiguration(String folderName) {
// split the name of the folder in segments.
- String[] folderSegments = folderName.split(FolderConfiguration.QUALIFIER_SEP);
+ String[] folderSegments = folderName.split(AndroidConstants.RES_QUALIFIER_SEP);
return setConfiguration(folderSegments);
}
@@ -657,7 +658,7 @@ public class ConfigurationSelector extends Composite {
public Image getColumnImage(Object element, int columnIndex) {
// only one column, so we can ignore columnIndex
if (element instanceof ResourceQualifier) {
- return ((ResourceQualifier)element).getIcon();
+ return ResourceHelper.getIcon(((ResourceQualifier)element).getClass());
}
return null;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java
index 1a9a78f..1d4fc13 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java
@@ -15,7 +15,8 @@
*/
package com.android.ide.eclipse.adt.internal.ui;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
+import com.android.ide.common.resources.ResourceRepository;
+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.resources.ResourceType;
@@ -196,11 +197,11 @@ public class MarginChooser extends SelectionStatusDialog implements Listener {
Button button = (Button) event.widget;
// Open a resource chooser dialog for specified resource type.
- IResourceRepository projectRepository = ResourceManager.getInstance()
+ ProjectResources projectRepository = ResourceManager.getInstance()
.getProjectResources(mProject);
- IResourceRepository systemRepository = mTargetData.getSystemResources();
+ ResourceRepository frameworkRepository = mTargetData.getFrameworkResources();
ResourceChooser dlg = new ResourceChooser(mProject, ResourceType.DIMEN,
- projectRepository, systemRepository, getShell());
+ projectRepository, frameworkRepository, getShell());
Text text = (Text) button.getData(PROP_TEXTFIELD);
dlg.setCurrentResource(text.getText().trim());
if (dlg.open() == Window.OK) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java
index 2a170a4..b436471 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java
@@ -16,12 +16,11 @@
package com.android.ide.eclipse.adt.internal.ui;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring;
import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
import com.android.resources.ResourceType;
import org.eclipse.core.resources.IProject;
@@ -52,6 +51,7 @@ import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.dialogs.SelectionStatusDialog;
+import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -66,7 +66,7 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
private static IDialogSettings sDialogSettings = new DialogSettings("");
- private IResourceRepository mResources;
+ private ResourceRepository mProjectResources;
private String mCurrentResource;
private FilteredTree mFilteredTree;
private Button mNewResButton;
@@ -77,10 +77,11 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
* @param project
* @param parent
*/
- public ReferenceChooserDialog(IProject project, IResourceRepository resources, Shell parent) {
+ public ReferenceChooserDialog(IProject project, ResourceRepository projectResources,
+ Shell parent) {
super(parent);
mProject = project;
- mResources = resources;
+ mProjectResources = projectResources;
int shellStyle = getShellStyle();
setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);
@@ -113,8 +114,7 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
ResourceType resourceType = (ResourceType)treeSelection.getFirstSegment();
ResourceItem resourceItem = (ResourceItem)treeSelection.getLastSegment();
- mCurrentResource = ResourceHelper.getXmlString(resourceType,
- resourceItem, false /* system */);
+ mCurrentResource = resourceItem.getXmlString(resourceType, false /* system */);
}
}
}
@@ -177,7 +177,7 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
mTreeViewer.setLabelProvider(new ResourceLabelProvider());
mTreeViewer.setContentProvider(new ResourceContentProvider(false /* fullLevels */));
- mTreeViewer.setInput(mResources);
+ mTreeViewer.setInput(mProjectResources);
}
protected void handleSelection() {
@@ -339,7 +339,8 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
*/
private void setupInitialSelection(ResourceType resourceType, String resourceName) {
// get all the resources of this type
- ResourceItem[] resourceItems = mResources.getResources(resourceType);
+ Collection<ResourceItem> resourceItems =
+ mProjectResources.getResourceItemsOfType(resourceType);
for (ResourceItem resourceItem : resourceItems) {
if (resourceName.equals(resourceItem.getName())) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
index 1a6a9de..5388b4e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
@@ -16,31 +16,24 @@
package com.android.ide.eclipse.adt.internal.ui;
-import static com.android.ide.eclipse.adt.AndroidConstants.EXT_XML;
-import static com.android.ide.eclipse.adt.AndroidConstants.WS_SEP;
-import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.NAME_ATTR;
-import static com.android.sdklib.SdkConstants.FD_RESOURCES;
-import static com.android.sdklib.SdkConstants.FD_VALUES;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors;
-import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring;
import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
+import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
import com.android.resources.ResourceType;
+import com.android.util.Pair;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.window.Window;
import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
@@ -60,77 +53,81 @@ import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
import org.eclipse.ui.dialogs.SelectionStatusDialog;
-import org.eclipse.wst.sse.core.StructuredModelManager;
-import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
-import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
-import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
-import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.w3c.dom.Text;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A dialog to let the user select a resource based on a resource type.
*/
-@SuppressWarnings("restriction") // XML model
public class ResourceChooser extends AbstractElementListSelectionDialog {
+ /** The return code from the dialog for the user choosing "Clear" */
+ public static final int CLEAR_RETURN_CODE = -5;
+ /** The dialog button ID for the user choosing "Clear" */
+ private static final int CLEAR_BUTTON_ID = CLEAR_RETURN_CODE;
private Pattern mProjectResourcePattern;
-
private ResourceType mResourceType;
-
- private IResourceRepository mProjectResources;
-
- private final static boolean SHOW_SYSTEM_RESOURCE = false; // TODO re-enable at some point
+ private final ResourceRepository mProjectResources;
+ private final ResourceRepository mFrameworkResources;
private Pattern mSystemResourcePattern;
- private IResourceRepository mSystemResources;
private Button mProjectButton;
private Button mSystemButton;
-
+ private Button mNewButton;
private String mCurrentResource;
-
private final IProject mProject;
+ private IInputValidator mInputValidator;
/**
* Creates a Resource Chooser dialog.
* @param project Project being worked on
* @param type The type of the resource to choose
* @param projectResources The repository for the project
- * @param systemResources The System resource repository
+ * @param frameworkResources The Framework resource repository
* @param parent the parent shell
*/
public ResourceChooser(IProject project, ResourceType type,
- IResourceRepository projectResources,
- IResourceRepository systemResources,
+ ResourceRepository projectResources,
+ ResourceRepository frameworkResources,
Shell parent) {
super(parent, new ResourceLabelProvider());
mProject = project;
mResourceType = type;
mProjectResources = projectResources;
+ mFrameworkResources = frameworkResources;
mProjectResourcePattern = Pattern.compile(
"@" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
- if (SHOW_SYSTEM_RESOURCE) {
- mSystemResources = systemResources;
- mSystemResourcePattern = Pattern.compile(
- "@android:" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
- }
+ mSystemResourcePattern = Pattern.compile(
+ "@android:" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
setTitle("Resource Chooser");
setMessage(String.format("Choose a %1$s resource",
mResourceType.getDisplayName().toLowerCase()));
}
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ createButton(parent, CLEAR_BUTTON_ID, "Clear", false /*defaultButton*/);
+ super.createButtonsForButtonBar(parent);
+ }
+
+ @Override
+ protected void buttonPressed(int buttonId) {
+ super.buttonPressed(buttonId);
+
+ if (buttonId == CLEAR_BUTTON_ID) {
+ assert CLEAR_RETURN_CODE != Window.OK && CLEAR_RETURN_CODE != Window.CANCEL;
+ setReturnCode(CLEAR_RETURN_CODE);
+ close();
+ }
+ }
+
public void setCurrentResource(String resource) {
mCurrentResource = resource;
}
@@ -139,14 +136,21 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
return mCurrentResource;
}
+ public void setInputValidator(IInputValidator inputValidator) {
+ mInputValidator = inputValidator;
+ }
+
@Override
protected void computeResult() {
Object[] elements = getSelectedElements();
if (elements.length == 1 && elements[0] instanceof ResourceItem) {
ResourceItem item = (ResourceItem)elements[0];
- mCurrentResource = ResourceHelper.getXmlString(mResourceType, item,
- SHOW_SYSTEM_RESOURCE && mSystemButton.getSelection());
+ mCurrentResource = item.getXmlString(mResourceType, mSystemButton.getSelection());
+
+ if (mInputValidator != null && mInputValidator.isValid(mCurrentResource) != null) {
+ mCurrentResource = null;
+ }
}
}
@@ -174,9 +178,6 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
* @param top the parent composite
*/
private void createButtons(Composite top) {
- if (!SHOW_SYSTEM_RESOURCE) {
- return;
- }
mProjectButton = new Button(top, SWT.RADIO);
mProjectButton.setText("Project Resources");
mProjectButton.addSelectionListener(new SelectionAdapter() {
@@ -184,7 +185,8 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
if (mProjectButton.getSelection()) {
- setListElements(mProjectResources.getResources(mResourceType));
+ setupResourceList();
+ mNewButton.setEnabled(true);
}
}
});
@@ -194,8 +196,9 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
- if (mProjectButton.getSelection()) {
- setListElements(mSystemResources.getResources(mResourceType));
+ if (mSystemButton.getSelection()) {
+ setupResourceList();
+ mNewButton.setEnabled(false);
}
}
});
@@ -206,16 +209,15 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
* @param top the parent composite
*/
private void createNewResButtons(Composite top) {
-
- Button newResButton = new Button(top, SWT.NONE);
+ mNewButton = new Button(top, SWT.NONE);
String title = String.format("New %1$s...", mResourceType.getDisplayName());
- newResButton.setText(title);
+ mNewButton.setText(title);
- // We only support adding new strings right now
- newResButton.setEnabled(Hyperlinks.isValueResource(mResourceType));
+ // We only support adding new values right now
+ mNewButton.setEnabled(ResourceHelper.isValueBasedResourceType(mResourceType));
- newResButton.addSelectionListener(new SelectionAdapter() {
+ mNewButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
@@ -223,18 +225,38 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
if (mResourceType == ResourceType.STRING) {
createNewString();
} else {
- assert Hyperlinks.isValueResource(mResourceType);
+ assert ResourceHelper.isValueBasedResourceType(mResourceType);
String newName = createNewValue(mResourceType);
if (newName != null) {
// Recompute the "current resource" to select the new id
- setupResourceList();
- selectItemName(newName);
+ ResourceItem[] items = setupResourceList();
+ selectItemName(newName, items);
}
}
}
});
}
+ @Override
+ protected void handleSelectionChanged() {
+ super.handleSelectionChanged();
+ if (mInputValidator != null) {
+ Object[] elements = getSelectedElements();
+ if (elements.length == 1 && elements[0] instanceof ResourceItem) {
+ ResourceItem item = (ResourceItem)elements[0];
+ String current = item.getXmlString(mResourceType, mSystemButton.getSelection());
+ String error = mInputValidator.isValid(current);
+ IStatus status;
+ if (error != null) {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error);
+ } else {
+ status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, null);
+ }
+ updateStatus(status);
+ }
+ }
+ }
+
private String createNewValue(ResourceType type) {
// Show a name/value dialog entering the key name and the value
Shell shell = AdtPlugin.getDisplay().getActiveShell();
@@ -252,101 +274,11 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
return null;
}
- // Find "dimens.xml" file in res/values/ (or corresponding name for other
- // value types)
- String fileName = type.getName() + 's';
- String projectPath = FD_RESOURCES + WS_SEP + FD_VALUES + WS_SEP + fileName + '.' + EXT_XML;
- IResource member = mProject.findMember(projectPath);
- if (member != null) {
- if (member instanceof IFile) {
- IFile file = (IFile) member;
- // File exists: Must add item to the XML
- IModelManager manager = StructuredModelManager.getModelManager();
- IStructuredModel model = null;
- try {
- model = manager.getExistingModelForEdit(file);
- if (model == null) {
- model = manager.getModelForEdit(file);
- }
- if (model instanceof IDOMModel) {
- model.beginRecording(this, String.format("Add %1$s",
- type.getDisplayName()));
- IDOMModel domModel = (IDOMModel) model;
- Document document = domModel.getDocument();
- Element root = document.getDocumentElement();
- IStructuredDocument structuredDocument = model.getStructuredDocument();
- Node lastElement = null;
- NodeList childNodes = root.getChildNodes();
- String indent = null;
- for (int i = childNodes.getLength() - 1; i >= 0; i--) {
- Node node = childNodes.item(i);
- if (node.getNodeType() == Node.ELEMENT_NODE) {
- lastElement = node;
- indent = AndroidXmlEditor.getIndent(structuredDocument, node);
- break;
- }
- }
- if (indent == null || indent.length() == 0) {
- indent = " "; //$NON-NLS-1$
- }
- Node nextChild = lastElement != null ? lastElement.getNextSibling() : null;
- Text indentNode = document.createTextNode('\n' + indent);
- root.insertBefore(indentNode, nextChild);
- Element element = document.createElement(Hyperlinks.getTagName(type));
- element.setAttribute(NAME_ATTR, name);
- root.insertBefore(element, nextChild);
- Text valueNode = document.createTextNode(value);
- element.appendChild(valueNode);
- model.save();
- return name;
- }
- } catch (Exception e) {
- AdtPlugin.log(e, "Cannot access XML value model");
- } finally {
- if (model != null) {
- model.endRecording(this);
- model.releaseFromEdit();
- }
- }
- }
-
- return null;
- } else {
- // No such file exists: just create it
- String prolog = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$
- StringBuilder sb = new StringBuilder(prolog);
-
- String root = ResourcesDescriptors.ROOT_ELEMENT;
- sb.append('<').append(root).append('>').append('\n');
- sb.append(" "); //$NON-NLS-1$
- sb.append('<');
- sb.append(type.getName());
- sb.append(" name=\""); //$NON-NLS-1$
- sb.append(name);
- sb.append('"');
- sb.append('>');
- sb.append(value);
- sb.append('<').append('/');
- sb.append(type.getName());
- sb.append(">\n"); //$NON-NLS-1$
- sb.append('<').append('/').append(root).append('>').append('\n');
- String result = sb.toString();
- String error = null;
- try {
- byte[] buf = result.getBytes("UTF8"); //$NON-NLS-1$
- InputStream stream = new ByteArrayInputStream(buf);
- IFile file = mProject.getFile(new Path(projectPath));
- file.create(stream, true /*force*/, null /*progress*/);
- return name;
- } catch (UnsupportedEncodingException e) {
- error = e.getMessage();
- } catch (CoreException e) {
- error = e.getMessage();
- }
-
- error = String.format("Failed to generate %1$s: %2$s", name, error);
- AdtPlugin.displayError("New Android XML File", error);
+ Pair<IFile, IRegion> resource = ResourceHelper.createResource(mProject, type, name, value);
+ if (resource != null) {
+ return name;
}
+
return null;
}
@@ -361,10 +293,10 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
IDialogConstants.OK_ID) {
// Recompute the "current resource" to select the new id
- setupResourceList();
+ ResourceItem[] items = setupResourceList();
// select it if possible
- selectItemName(ref.getXmlStringId());
+ selectItemName(ref.getXmlStringId(), items);
}
} catch (InterruptedException ex) {
// Interrupted. Pass.
@@ -372,37 +304,38 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
}
/**
- * @return The repository currently selected.
+ * Setups the current list.
*/
- private IResourceRepository getCurrentRepository() {
- IResourceRepository repo = mProjectResources;
+ private ResourceItem[] setupResourceList() {
+ Collection<ResourceItem> items = null;
+ if (mProjectButton.getSelection()) {
+ items = mProjectResources.getResourceItemsOfType(mResourceType);
+ } else if (mSystemButton.getSelection()) {
+ items = mFrameworkResources.getResourceItemsOfType(mResourceType);
+ }
- if (SHOW_SYSTEM_RESOURCE && mSystemButton.getSelection()) {
- repo = mSystemResources;
+ if (items == null) {
+ items = Collections.emptyList();
}
- return repo;
- }
- /**
- * Setups the current list.
- */
- private void setupResourceList() {
- IResourceRepository repo = getCurrentRepository();
- setListElements(repo.getResources(mResourceType));
+ ResourceItem[] arrayItems = items.toArray(new ResourceItem[items.size()]);
+
+ // sort the array
+ Arrays.sort(arrayItems);
+
+ setListElements(arrayItems);
+
+ return arrayItems;
}
/**
* Select an item by its name, if possible.
*/
- private void selectItemName(String itemName) {
- if (itemName == null) {
+ private void selectItemName(String itemName, ResourceItem[] items) {
+ if (itemName == null || items == null) {
return;
}
- IResourceRepository repo = getCurrentRepository();
-
- ResourceItem[] items = repo.getResources(mResourceType);
-
for (ResourceItem item : items) {
if (itemName.equals(item.getName())) {
setSelection(new Object[] { item });
@@ -419,46 +352,46 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
boolean isSystem = false;
String itemName = null;
- // Is this a system resource?
- // If not a system resource or if they are not available, this will be a project res.
- if (SHOW_SYSTEM_RESOURCE) {
+ if (resourceString != null) {
+ // Is this a system resource?
+ // If not a system resource or if they are not available, this will be a project res.
Matcher m = mSystemResourcePattern.matcher(resourceString);
if (m.matches()) {
itemName = m.group(1);
isSystem = true;
}
- }
- if (!isSystem && itemName == null) {
- // Try to match project resource name
- Matcher m = mProjectResourcePattern.matcher(resourceString);
- if (m.matches()) {
- itemName = m.group(1);
+ if (!isSystem && itemName == null) {
+ // Try to match project resource name
+ m = mProjectResourcePattern.matcher(resourceString);
+ if (m.matches()) {
+ itemName = m.group(1);
+ }
}
}
// Update the repository selection
- if (SHOW_SYSTEM_RESOURCE) {
- mProjectButton.setSelection(!isSystem);
- mSystemButton.setSelection(isSystem);
- }
+ mProjectButton.setSelection(!isSystem);
+ mSystemButton.setSelection(isSystem);
+ mNewButton.setEnabled(!isSystem);
// Update the list
- setupResourceList();
+ ResourceItem[] items = setupResourceList();
// If we have a selection name, select it
if (itemName != null) {
- selectItemName(itemName);
+ selectItemName(itemName, items);
}
}
/** Dialog asking for a Name/Value pair */
- private static class NameValueDialog extends SelectionStatusDialog implements Listener {
+ private class NameValueDialog extends SelectionStatusDialog implements Listener {
private org.eclipse.swt.widgets.Text mNameText;
private org.eclipse.swt.widgets.Text mValueText;
private String mInitialName;
private String mName;
private String mValue;
+ private ResourceNameValidator mValidator;
public NameValueDialog(Shell parent, String initialName) {
super(parent);
@@ -471,7 +404,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
container.setLayout(new GridLayout(2, false));
GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
// Wide enough to accommodate the error label
- gridData.widthHint = 400;
+ gridData.widthHint = 500;
container.setLayoutData(gridData);
@@ -527,7 +460,15 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
} else if (mValue.length() == 0) {
status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Enter a value");
} else {
- status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, null);
+ if (mValidator == null) {
+ mValidator = ResourceNameValidator.create(false, mProject, mResourceType);
+ }
+ String error = mValidator.isValid(mName);
+ if (error != null) {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error);
+ } else {
+ status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, null);
+ }
}
updateStatus(status);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java
index f57b74e..6eaa756 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java
@@ -16,15 +16,18 @@
package com.android.ide.eclipse.adt.internal.ui;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.ConfigurableResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
import com.android.resources.ResourceType;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
/**
* Content provider for the Resource Explorer TreeView.
* Each level of the tree is represented by a different class.
@@ -40,10 +43,10 @@ import org.eclipse.jface.viewers.Viewer;
* <li>{@link ResourceFile}. (optional) This represents a particular version of the
* {@link ResourceItem}. It is displayed as a list of resource qualifier.
* </li>
- * </ul>
- * </ul>
- * </ul>
- *
+ * </ul>
+ * </ul>
+ * </ul>
+ *
* @see ResourceLabelProvider
*/
public class ResourceContentProvider implements ITreeContentProvider {
@@ -51,10 +54,10 @@ public class ResourceContentProvider implements ITreeContentProvider {
/**
* The current ProjectResources being displayed.
*/
- private IResourceRepository mResources;
-
+ private ResourceRepository mResources;
+
private boolean mFullLevels;
-
+
/**
* Constructs a new content providers for resource display.
* @param fullLevels if <code>true</code> the content provider will suppport all 3 levels. If
@@ -66,9 +69,12 @@ public class ResourceContentProvider implements ITreeContentProvider {
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof ResourceType) {
- return mResources.getResources((ResourceType)parentElement);
- } else if (mFullLevels && parentElement instanceof ConfigurableResourceItem) {
- return ((ConfigurableResourceItem)parentElement).getSourceFileArray();
+ Object[] array = mResources.getResourceItemsOfType(
+ (ResourceType)parentElement).toArray();
+ Arrays.sort(array);
+ return array;
+ } else if (mFullLevels && parentElement instanceof ResourceItem) {
+ return ((ResourceItem)parentElement).getSourceFileArray();
}
return null;
}
@@ -80,18 +86,20 @@ public class ResourceContentProvider implements ITreeContentProvider {
public boolean hasChildren(Object element) {
if (element instanceof ResourceType) {
- return mResources.hasResources((ResourceType)element);
- } else if (mFullLevels && element instanceof ConfigurableResourceItem) {
- return ((ConfigurableResourceItem)element).hasAlternates();
+ return mResources.hasResourcesOfType((ResourceType)element);
+ } else if (mFullLevels && element instanceof ResourceItem) {
+ return ((ResourceItem)element).hasAlternates();
}
return false;
}
public Object[] getElements(Object inputElement) {
- if (inputElement instanceof IResourceRepository) {
- if ((IResourceRepository)inputElement == mResources) {
+ if (inputElement instanceof ResourceRepository) {
+ if ((ResourceRepository)inputElement == mResources) {
// get the top level resources.
- return mResources.getAvailableResourceTypes();
+ List<ResourceType> types = mResources.getAvailableResourceTypes();
+ Collections.sort(types);
+ return types.toArray();
}
}
@@ -103,8 +111,8 @@ public class ResourceContentProvider implements ITreeContentProvider {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- if (newInput instanceof IResourceRepository) {
- mResources = (IResourceRepository)newInput;
+ if (newInput instanceof ResourceRepository) {
+ mResources = (ResourceRepository)newInput;
}
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java
index f1810a1..df8a97a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java
@@ -16,15 +16,15 @@
package com.android.ide.eclipse.adt.internal.ui;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceItem;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResourceItem;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IResourceEventListener;
import com.android.ide.eclipse.adt.io.IFileWrapper;
-import com.android.sdklib.io.IAbstractFile;
+import com.android.io.IAbstractFile;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -76,9 +76,9 @@ public class ResourceExplorerView extends ViewPart implements ISelectionListener
// Note: keep using the obsolete AndroidConstants.EDITORS_NAMESPACE (which used
// to be the Editors Plugin ID) to keep existing preferences functional.
private final static String PREFS_COLUMN_RES =
- AndroidConstants.EDITORS_NAMESPACE + "ResourceExplorer.Col1"; //$NON-NLS-1$
+ AdtConstants.EDITORS_NAMESPACE + "ResourceExplorer.Col1"; //$NON-NLS-1$
private final static String PREFS_COLUMN_2 =
- AndroidConstants.EDITORS_NAMESPACE + "ResourceExplorer.Col2"; //$NON-NLS-1$
+ AdtConstants.EDITORS_NAMESPACE + "ResourceExplorer.Col2"; //$NON-NLS-1$
private Tree mTree;
private TreeViewer mTreeViewer;
@@ -138,10 +138,10 @@ public class ResourceExplorerView extends ViewPart implements ISelectionListener
}
} catch (PartInitException e) {
}
- } else if (element instanceof ProjectResourceItem) {
+ } else if (element instanceof ResourceItem) {
// if it's a ResourceItem, we open the first file, but only if
// there's no alternate files.
- ProjectResourceItem item = (ProjectResourceItem)element;
+ ResourceItem item = (ResourceItem)element;
if (item.isEditableDirectly()) {
ResourceFile[] files = item.getSourceFileArray();
@@ -240,7 +240,7 @@ public class ResourceExplorerView extends ViewPart implements ISelectionListener
try {
// if it's an android project, then we get its resources, and feed them
// to the tree viewer.
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT)) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT)) {
if (mCurrentProject != project) {
ProjectResources projRes = ResourceManager.getInstance().getProjectResources(
project);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java
index 50e1d07..4453acb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java
@@ -16,11 +16,8 @@
package com.android.ide.eclipse.adt.internal.ui;
-import com.android.ide.eclipse.adt.internal.resources.IIdResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.ConfigurableResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.IdResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceItem;
import com.android.resources.ResourceType;
import org.eclipse.jface.viewers.ILabelProvider;
@@ -47,15 +44,15 @@ import org.eclipse.ui.PlatformUI;
* <li>{@link ResourceFile}. This represents a particular version of the {@link ResourceItem}.
* It is displayed as a list of resource qualifier.
* </li>
- * </ul>
- * </ul>
- * </ul>
- *
+ * </ul>
+ * </ul>
+ * </ul>
+ *
* @see ResourceContentProvider
*/
public class ResourceLabelProvider implements ILabelProvider, ITableLabelProvider {
private Image mWarningImage;
-
+
public ResourceLabelProvider() {
mWarningImage = PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(
ISharedImages.IMG_OBJS_WARN_TSK).createImage();
@@ -94,8 +91,8 @@ public class ResourceLabelProvider implements ILabelProvider, ITableLabelProvide
public Image getColumnImage(Object element, int columnIndex) {
if (columnIndex == 1) {
- if (element instanceof ConfigurableResourceItem) {
- ConfigurableResourceItem item = (ConfigurableResourceItem)element;
+ if (element instanceof ResourceItem) {
+ ResourceItem item = (ResourceItem)element;
if (item.hasDefault() == false) {
return mWarningImage;
}
@@ -116,19 +113,18 @@ public class ResourceLabelProvider implements ILabelProvider, ITableLabelProvide
}
break;
case 1:
- if (element instanceof ConfigurableResourceItem) {
- ConfigurableResourceItem item = (ConfigurableResourceItem)element;
- int count = item.getAlternateCount();
- if (count > 0) {
- if (item.hasDefault()) {
- count++;
- }
- return String.format("%1$d version(s)", count);
- }
- } else if (element instanceof IIdResourceItem) {
- IIdResourceItem idResource = (IIdResourceItem)element;
- if (idResource.isDeclaredInline()) {
+ if (element instanceof ResourceItem) {
+ ResourceItem item = (ResourceItem)element;
+ if (item.isDeclaredInline()) {
return "Declared inline";
+ } else {
+ int count = item.getAlternateCount();
+ if (count > 0) {
+ if (item.hasDefault()) {
+ count++;
+ }
+ return String.format("%1$d version(s)", count);
+ }
}
}
return null;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
index 4a2a77a..c1ce672 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
@@ -59,7 +59,7 @@ import java.util.List;
*/
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 PROJECT_LOGO_LARGE = "icons/android-64.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$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ProjectCheckPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ProjectCheckPage.java
index 052fc50..3326c6f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ProjectCheckPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ProjectCheckPage.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.internal.wizards.export;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
@@ -160,7 +160,7 @@ final class ProjectCheckPage extends ExportWizardPage {
mHasMessage = true;
} else {
try {
- if (project.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
addError(mErrorComposite, "Project is not an Android project.");
} else {
// check for errors
@@ -173,7 +173,7 @@ final class ProjectCheckPage extends ExportWizardPage {
if (outputIFolder != null) {
String outputOsPath = outputIFolder.getLocation().toOSString();
String apkFilePath = outputOsPath + File.separator + project.getName() +
- AndroidConstants.DOT_ANDROID_PACKAGE;
+ AdtConstants.DOT_ANDROID_PACKAGE;
File f = new File(apkFilePath);
if (f.isFile() == false) {
@@ -181,7 +181,7 @@ final class ProjectCheckPage extends ExportWizardPage {
String.format("%1$s/%2$s/%1$s%3$s does not exists!",
project.getName(),
outputIFolder.getName(),
- AndroidConstants.DOT_ANDROID_PACKAGE));
+ AdtConstants.DOT_ANDROID_PACKAGE));
}
} else {
addError(mErrorComposite,
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java
index d17e3cf..c02f773 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java
@@ -22,8 +22,9 @@
package com.android.ide.eclipse.adt.internal.wizards.newproject;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
@@ -48,6 +49,7 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.JavaConventions;
+import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.TextProcessor;
import org.eclipse.swt.SWT;
@@ -69,11 +71,16 @@ import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkingSet;
import java.io.File;
import java.io.FileFilter;
import java.net.URI;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.regex.Pattern;
/**
@@ -92,6 +99,8 @@ import java.util.regex.Pattern;
* Do not derive from this class.
*/
public class NewProjectCreationPage extends WizardPage {
+ /** Suffix added by default to activity names */
+ private static final String ACTIVITY_NAME_SUFFIX = "Activity"; //$NON-NLS-1$
// constants
private static final String MAIN_PAGE_NAME = "newAndroidProjectPage"; //$NON-NLS-1$
@@ -154,12 +163,15 @@ public class NewProjectCreationPage extends WizardPage {
private boolean mInternalApplicationNameUpdate;
private boolean mInternalCreateActivityUpdate;
private boolean mInternalActivityNameUpdate;
+ private boolean mInternalMinSdkUpdate;
private boolean mProjectNameModifiedByUser;
private boolean mApplicationNameModifiedByUser;
+ private boolean mActivityNameModifiedByUser;
+ private boolean mMinSdkModifiedByUser;
private final ArrayList<String> mSamplesPaths = new ArrayList<String>();
private Combo mSamplesCombo;
-
+ private WorkingSetGroup mWorkingSetGroup;
/**
@@ -170,6 +182,12 @@ public class NewProjectCreationPage extends WizardPage {
setPageComplete(false);
setTitle("New Android Project");
setDescription("Creates a new Android Project resource.");
+ mWorkingSetGroup = new WorkingSetGroup();
+ setWorkingSets(new IWorkingSet[0]);
+ }
+
+ public void init(IStructuredSelection selection, IWorkbenchPart activePart) {
+ setWorkingSets(WorkingSetHelper.getSelectedWorkingSet(selection, activePart));
}
// --- Getters used by NewProjectWizard ---
@@ -212,6 +230,9 @@ public class NewProjectCreationPage extends WizardPage {
public String getSourceFolder();
/** Returns the current sdk target or null if none has been selected yet. */
public IAndroidTarget getSdkTarget();
+ /** Returns the current working sets or null if none has been selected yet. */
+ public IWorkingSet[] getSelectedWorkingSets();
+
}
@@ -297,6 +318,11 @@ public class NewProjectCreationPage extends WizardPage {
public IAndroidTarget getSdkTarget() {
return mSdkTargetSelector == null ? null : mSdkTargetSelector.getSelected();
}
+
+ /** Returns the current sdk target or null if none has been selected yet. */
+ public IWorkingSet[] getSelectedWorkingSets() {
+ return getWorkingSets();
+ }
}
/**
@@ -354,6 +380,7 @@ public class NewProjectCreationPage extends WizardPage {
createLocationGroup(composite);
createTargetGroup(composite);
createPropertiesGroup(composite);
+ createWorkingSetGroup(composite);
// Update state the first time
enableLocationWidgets();
@@ -415,15 +442,11 @@ public class NewProjectCreationPage extends WizardPage {
mProjectNameField.setFont(parent.getFont());
mProjectNameField.addListener(SWT.Modify, new Listener() {
public void handleEvent(Event event) {
- if (!mInternalProjectNameUpdate) {
- mProjectNameModifiedByUser = true;
- }
- updateLocationPathField(null);
+ onProjectFieldModified();
}
});
}
-
/**
* Creates the group for the Project options:
* [radio] Create new project
@@ -514,7 +537,11 @@ public class NewProjectCreationPage extends WizardPage {
new Label(samples_group, SWT.NONE).setText("Samples:");
- mSamplesCombo = new Combo(samples_group, SWT.DROP_DOWN | SWT.READ_ONLY);
+ if (Platform.getWS().equals(Platform.WS_GTK)) {
+ mSamplesCombo = new Combo(samples_group, SWT.SIMPLE | SWT.READ_ONLY);
+ } else {
+ mSamplesCombo = new Combo(samples_group, SWT.DROP_DOWN | SWT.READ_ONLY);
+ }
mSamplesCombo.setEnabled(false);
mSamplesCombo.select(0);
mSamplesCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
@@ -615,9 +642,7 @@ public class NewProjectCreationPage extends WizardPage {
mApplicationNameField.setFont(parent.getFont());
mApplicationNameField.addListener(SWT.Modify, new Listener() {
public void handleEvent(Event event) {
- if (!mInternalApplicationNameUpdate) {
- mApplicationNameModifiedByUser = true;
- }
+ onApplicationFieldModified();
}
});
@@ -678,11 +703,17 @@ public class NewProjectCreationPage extends WizardPage {
mMinSdkVersionField.setFont(parent.getFont());
mMinSdkVersionField.addListener(SWT.Modify, new Listener() {
public void handleEvent(Event event) {
+ onMinSdkFieldUpdated();
validatePageComplete();
}
});
}
+ private void createWorkingSetGroup(final Composite composite) {
+ Composite group = mWorkingSetGroup.createControl(composite);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ }
+
//--- Internal getters & setters ------------------
@@ -870,6 +901,54 @@ public class NewProjectCreationPage extends WizardPage {
}
}
+ private void onProjectFieldModified() {
+ if (!mInternalProjectNameUpdate) {
+ mProjectNameModifiedByUser = true;
+
+ if (!mApplicationNameModifiedByUser) {
+ String name = DescriptorsUtils.capitalize(mProjectNameField.getText());
+ try {
+ mInternalApplicationNameUpdate = true;
+ mApplicationNameField.setText(name);
+ } finally {
+ mInternalApplicationNameUpdate = false;
+ }
+ }
+ if (!mActivityNameModifiedByUser) {
+ String name = DescriptorsUtils.capitalize(mProjectNameField.getText());
+ try {
+ mInternalActivityNameUpdate = true;
+ mActivityNameField.setText(name + ACTIVITY_NAME_SUFFIX);
+ } finally {
+ mInternalActivityNameUpdate = false;
+ }
+
+ }
+ }
+ updateLocationPathField(null);
+ }
+
+ private void onMinSdkFieldUpdated() {
+ if (!mInternalMinSdkUpdate) {
+ mMinSdkModifiedByUser = true;
+ }
+ }
+
+ private void onApplicationFieldModified() {
+ if (!mInternalApplicationNameUpdate) {
+ mApplicationNameModifiedByUser = true;
+ if (!mActivityNameModifiedByUser) {
+ String name = DescriptorsUtils.capitalize(mApplicationNameField.getText());
+ try {
+ mInternalActivityNameUpdate = true;
+ mActivityNameField.setText(name + ACTIVITY_NAME_SUFFIX);
+ } finally {
+ mInternalActivityNameUpdate = false;
+ }
+ }
+ }
+ }
+
/**
* The location path field is either modified internally (from updateLocationPathField)
* or manually by the user when the custom_location mode is not set.
@@ -928,6 +1007,10 @@ public class NewProjectCreationPage extends WizardPage {
* validate the page.
*/
private void onActivityNameFieldModified() {
+ if (!mInternalActivityNameUpdate) {
+ mActivityNameModifiedByUser = true;
+ }
+
if (mInfo.isNewProject() && !mInternalActivityNameUpdate) {
mUserActivityName = mInfo.getActivityName();
validatePageComplete();
@@ -943,6 +1026,40 @@ public class NewProjectCreationPage extends WizardPage {
private void onSdkTargetModified() {
IAndroidTarget target = mInfo.getSdkTarget();
+ // Update the minimum SDK text field?
+ // We do if one of two conditions are met:
+ if (target != null) {
+ boolean setMinSdk = false;
+ int apiLevel = target.getVersion().getApiLevel();
+ // 1. Has the user not manually edited the SDK field yet? If so, keep
+ // updating it to the selected value.
+ if (!mMinSdkModifiedByUser) {
+ setMinSdk = true;
+ } else {
+ // 2. Is the API level set to a higher level than the newly selected
+ // target SDK? If so, change it down to the new lower value.
+ String s = mMinSdkVersionField.getText().trim();
+ if (s.length() > 0) {
+ try {
+ int currentApi = Integer.parseInt(s);
+ if (currentApi > apiLevel) {
+ setMinSdk = true;
+ }
+ } catch (NumberFormatException nfe) {
+ // User may have typed something invalid -- ignore
+ }
+ }
+ }
+ if (setMinSdk) {
+ try {
+ mInternalMinSdkUpdate = true;
+ mMinSdkVersionField.setText(Integer.toString(apiLevel));
+ } finally {
+ mInternalMinSdkUpdate = false;
+ }
+ }
+ }
+
loadSamplesForTarget(target);
enableLocationWidgets();
onSampleSelected();
@@ -1040,7 +1157,7 @@ public class NewProjectCreationPage extends WizardPage {
// name as a default. If the activity name has dots, it's a part of a
// package specification and only the last identifier must be used.
if (activityName.indexOf('.') != -1) {
- String[] ids = activityName.split(AndroidConstants.RE_DOT);
+ String[] ids = activityName.split(AdtConstants.RE_DOT);
activityName = ids[ids.length - 1];
}
if (mProjectNameField.getText().length() == 0 || !mProjectNameModifiedByUser) {
@@ -1178,6 +1295,8 @@ public class NewProjectCreationPage extends WizardPage {
mSamplesCombo.add("This target has no samples. Please select another target.");
mSamplesCombo.select(0);
return;
+ } else {
+ Collections.sort(mSamplesPaths);
}
// Recompute the description of each sample (the relative path
@@ -1185,6 +1304,7 @@ public class NewProjectCreationPage extends WizardPage {
int selIndex = 0;
int i = 0;
int n = samplesRootPath.length();
+ Set<String> paths = new TreeSet<String>();
for (String path : mSamplesPaths) {
if (path.length() > n) {
path = path.substring(n);
@@ -1201,10 +1321,10 @@ public class NewProjectCreationPage extends WizardPage {
selIndex = i;
}
- mSamplesCombo.add(path);
+ paths.add(path);
i++;
}
-
+ mSamplesCombo.setItems(paths.toArray(new String[0]));
mSamplesCombo.select(selIndex);
} else {
@@ -1487,6 +1607,10 @@ public class NewProjectCreationPage extends WizardPage {
return setStatus("Activity name must be specified.", MSG_ERROR);
}
+ if (ACTIVITY_NAME_SUFFIX.equals(activityFieldContents)) {
+ return setStatus("Enter a valid activity name", MSG_ERROR);
+ }
+
// The activity field can actually contain part of a sub-package name
// or it can start with a dot "." to indicates it comes from the parent package name.
String packageName = ""; //$NON-NLS-1$
@@ -1588,7 +1712,7 @@ public class NewProjectCreationPage extends WizardPage {
} else if (osTarget.indexOf('.') == 0) {
osTarget = mInfo.getPackageName() + osTarget;
}
- osTarget = osTarget.replace('.', File.separatorChar) + AndroidConstants.DOT_JAVA;
+ osTarget = osTarget.replace('.', File.separatorChar) + AdtConstants.DOT_JAVA;
String projectPath = getProjectLocation();
File projectDir = new File(projectPath);
@@ -1634,4 +1758,23 @@ public class NewProjectCreationPage extends WizardPage {
return messageType;
}
+ /**
+ * Returns the working sets to which the new project should be added.
+ *
+ * @return the selected working sets to which the new project should be added
+ */
+ public IWorkingSet[] getWorkingSets() {
+ return mWorkingSetGroup.getSelectedWorkingSets();
+ }
+
+ /**
+ * Sets the working sets to which the new project should be added.
+ *
+ * @param workingSets the initial selected working sets
+ */
+ public void setWorkingSets(IWorkingSet[] workingSets) {
+ assert workingSets != null;
+ mWorkingSetGroup.setWorkingSets(workingSets);
+ }
+
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java
index 7c35ac8..9a3f572 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java
@@ -16,18 +16,19 @@
package com.android.ide.eclipse.adt.internal.wizards.newproject;
+import com.android.AndroidConstants;
import com.android.ide.common.layout.LayoutConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.project.AndroidNature;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreationPage.IMainInfo;
import com.android.ide.eclipse.adt.internal.wizards.newproject.NewTestProjectCreationPage.TestInfo;
+import com.android.io.StreamException;
import com.android.resources.Density;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.StreamException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -57,8 +58,11 @@ import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkingSet;
+import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import java.io.ByteArrayInputStream;
@@ -80,8 +84,8 @@ import java.util.Map.Entry;
* Note: this class is public so that it can be accessed from unit tests.
* It is however an internal class. Its API may change without notice.
* It should semantically be considered as a private final class.
+ * <p/>
* Do not derive from this class.
-
*/
public class NewProjectWizard extends Wizard implements INewWizard {
@@ -107,6 +111,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
private static final String PARAM_ACTIVITY = "ACTIVITY_NAME"; //$NON-NLS-1$
private static final String PARAM_APPLICATION = "APPLICATION_NAME"; //$NON-NLS-1$
private static final String PARAM_PACKAGE = "PACKAGE"; //$NON-NLS-1$
+ private static final String PARAM_IMPORT_RESOURCE_CLASS = "IMPORT_RESOURCE_CLASS"; //$NON-NLS-1$
private static final String PARAM_PROJECT = "PROJECT_NAME"; //$NON-NLS-1$
private static final String PARAM_STRING_NAME = "STRING_NAME"; //$NON-NLS-1$
private static final String PARAM_STRING_CONTENT = "STRING_CONTENT"; //$NON-NLS-1$
@@ -130,25 +135,28 @@ public class NewProjectWizard extends Wizard implements INewWizard {
private static final String PH_TEST_INSTRUMENTATION = "TEST-INSTRUMENTATION"; //$NON-NLS-1$
private static final String BIN_DIRECTORY =
- SdkConstants.FD_OUTPUT + AndroidConstants.WS_SEP;
+ SdkConstants.FD_OUTPUT + AdtConstants.WS_SEP;
private static final String RES_DIRECTORY =
- SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
+ SdkConstants.FD_RESOURCES + AdtConstants.WS_SEP;
private static final String ASSETS_DIRECTORY =
- SdkConstants.FD_ASSETS + AndroidConstants.WS_SEP;
+ SdkConstants.FD_ASSETS + AdtConstants.WS_SEP;
private static final String DRAWABLE_DIRECTORY =
- SdkConstants.FD_DRAWABLE + AndroidConstants.WS_SEP;
+ AndroidConstants.FD_RES_DRAWABLE + AdtConstants.WS_SEP;
private static final String DRAWABLE_HDPI_DIRECTORY =
- SdkConstants.FD_DRAWABLE + "-" + Density.HIGH.getResourceValue() + AndroidConstants.WS_SEP; //$NON-NLS-1$
+ AndroidConstants.FD_RES_DRAWABLE + "-" + Density.HIGH.getResourceValue() + //$NON-NLS-1$
+ AdtConstants.WS_SEP;
private static final String DRAWABLE_MDPI_DIRECTORY =
- SdkConstants.FD_DRAWABLE + "-" + Density.MEDIUM.getResourceValue() + AndroidConstants.WS_SEP; //$NON-NLS-1$
+ AndroidConstants.FD_RES_DRAWABLE + "-" + Density.MEDIUM.getResourceValue() + //$NON-NLS-1$
+ AdtConstants.WS_SEP;
private static final String DRAWABLE_LDPI_DIRECTORY =
- SdkConstants.FD_DRAWABLE + "-" + Density.LOW.getResourceValue() + AndroidConstants.WS_SEP; //$NON-NLS-1$
+ AndroidConstants.FD_RES_DRAWABLE + "-" + Density.LOW.getResourceValue() + //$NON-NLS-1$
+ AdtConstants.WS_SEP;
private static final String LAYOUT_DIRECTORY =
- SdkConstants.FD_LAYOUT + AndroidConstants.WS_SEP;
+ AndroidConstants.FD_RES_LAYOUT + AdtConstants.WS_SEP;
private static final String VALUES_DIRECTORY =
- SdkConstants.FD_VALUES + AndroidConstants.WS_SEP;
+ AndroidConstants.FD_RES_VALUES + AdtConstants.WS_SEP;
private static final String GEN_SRC_DIRECTORY =
- SdkConstants.FD_GEN_SOURCES + AndroidConstants.WS_SEP;
+ SdkConstants.FD_GEN_SOURCES + AdtConstants.WS_SEP;
private static final String TEMPLATES_DIRECTORY = "templates/"; //$NON-NLS-1$
private static final String TEMPLATE_MANIFEST = TEMPLATES_DIRECTORY
@@ -189,7 +197,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
DRAWABLE_HDPI_DIRECTORY, DRAWABLE_MDPI_DIRECTORY, DRAWABLE_LDPI_DIRECTORY,
LAYOUT_DIRECTORY, VALUES_DIRECTORY };
- private static final String PROJECT_LOGO_LARGE = "icons/android_large.png"; //$NON-NLS-1$
+ private static final String PROJECT_LOGO_LARGE = "icons/android-64.png"; //$NON-NLS-1$
private static final String JAVA_ACTIVITY_TEMPLATE = "java_file.template"; //$NON-NLS-1$
private static final String LAYOUT_TEMPLATE = "layout.template"; //$NON-NLS-1$
private static final String MAIN_LAYOUT_XML = "main.xml"; //$NON-NLS-1$
@@ -416,13 +424,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
parameters.put(PARAM_MIN_SDK_VERSION, info.getMinSdkVersion());
if (info.isCreateActivity()) {
- // An activity name can be of the form ".package.Class" or ".Class".
- // The initial dot is ignored, as it is always added later in the templates.
- String activityName = info.getActivityName();
- if (activityName.startsWith(".")) { //$NON-NLS-1$
- activityName = activityName.substring(1);
- }
- parameters.put(PARAM_ACTIVITY, activityName);
+ parameters.put(PARAM_ACTIVITY, info.getActivityName());
}
// create a dictionary of string that will contain name+content.
@@ -432,7 +434,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
IPath path = info.getLocationPath();
IPath defaultLocation = Platform.getLocation();
- if (!path.equals(defaultLocation)) {
+ if (path != null && !path.equals(defaultLocation)) {
description.setLocation(path);
}
@@ -574,6 +576,21 @@ public class NewProjectWizard extends Wizard implements INewWizard {
mainData.getDescription(),
mainData.getParameters(),
mainData.getDictionary());
+
+ if (mainProject != null) {
+ final IJavaProject javaProject = JavaCore.create(mainProject);
+ Display.getDefault().syncExec(new Runnable() {
+
+ public void run() {
+ IWorkingSet[] workingSets = mMainPage.getWorkingSets();
+ if (workingSets.length > 0 && javaProject != null
+ && javaProject.exists()) {
+ PlatformUI.getWorkbench().getWorkingSetManager()
+ .addToWorkingSets(javaProject, workingSets);
+ }
+ }
+ });
+ }
}
if (testData != null) {
@@ -583,14 +600,27 @@ public class NewProjectWizard extends Wizard implements INewWizard {
parameters.put(PARAM_REFERENCE_PROJECT, mainProject);
}
- createEclipseProject(
+ IProject testProject = createEclipseProject(
new SubProgressMonitor(monitor, 50),
testData.getProject(),
testData.getDescription(),
parameters,
testData.getDictionary());
+ if (testProject != null) {
+ final IJavaProject javaProject = JavaCore.create(testProject);
+ Display.getDefault().syncExec(new Runnable() {
+
+ public void run() {
+ IWorkingSet[] workingSets = mTestPage.getWorkingSets();
+ if (workingSets.length > 0 && javaProject != null
+ && javaProject.exists()) {
+ PlatformUI.getWorkbench().getWorkingSetManager()
+ .addToWorkingSets(javaProject, workingSets);
+ }
+ }
+ });
+ }
}
-
} catch (CoreException e) {
throw new InvocationTargetException(e);
} catch (IOException e) {
@@ -635,12 +665,12 @@ public class NewProjectWizard extends Wizard implements INewWizard {
AndroidNature.setupProjectNatures(project, monitor);
// Create folders in the project if they don't already exist
- addDefaultDirectories(project, AndroidConstants.WS_ROOT, DEFAULT_DIRECTORIES, monitor);
+ addDefaultDirectories(project, AdtConstants.WS_ROOT, DEFAULT_DIRECTORIES, monitor);
String[] sourceFolders = new String[] {
(String) parameters.get(PARAM_SRC_FOLDER),
GEN_SRC_DIRECTORY
};
- addDefaultDirectories(project, AndroidConstants.WS_ROOT, sourceFolders, monitor);
+ addDefaultDirectories(project, AdtConstants.WS_ROOT, sourceFolders, monitor);
// Create the resource folders in the project if they don't already exist.
if (legacy) {
@@ -778,6 +808,15 @@ public class NewProjectWizard extends Wizard implements INewWizard {
// now get the activity template
String activityTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_ACTIVITIES);
+ // If the activity name doesn't contain any dot, it's in the form
+ // "ClassName" and we need to expand it to ".ClassName" in the XML.
+ String name = (String) parameters.get(PARAM_ACTIVITY);
+ if (name.indexOf('.') == -1) {
+ // Duplicate the parameters map to avoid changing the caller
+ parameters = new HashMap<String, Object>(parameters);
+ parameters.put(PARAM_ACTIVITY, "." + name); //$NON-NLS-1$
+ }
+
// Replace all keyword parameters to make main activity.
String activities = replaceParameters(activityTemplate, parameters);
@@ -856,8 +895,8 @@ public class NewProjectWizard extends Wizard implements INewWizard {
throws CoreException, IOException {
// create the IFile object and check if the file doesn't already exist.
- IFile file = project.getFile(RES_DIRECTORY + AndroidConstants.WS_SEP
- + VALUES_DIRECTORY + AndroidConstants.WS_SEP + STRINGS_FILE);
+ IFile file = project.getFile(RES_DIRECTORY + AdtConstants.WS_SEP
+ + VALUES_DIRECTORY + AdtConstants.WS_SEP + STRINGS_FILE);
if (!file.exists()) {
// get the Strings.xml template
String stringDefinitionTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_STRINGS);
@@ -909,8 +948,8 @@ public class NewProjectWizard extends Wizard implements INewWizard {
throws CoreException {
if (legacy) { // density support
// do medium density icon only, in the default drawable folder.
- IFile file = project.getFile(RES_DIRECTORY + AndroidConstants.WS_SEP
- + DRAWABLE_DIRECTORY + AndroidConstants.WS_SEP + PROJECT_ICON);
+ IFile file = project.getFile(RES_DIRECTORY + AdtConstants.WS_SEP
+ + DRAWABLE_DIRECTORY + AdtConstants.WS_SEP + PROJECT_ICON);
if (!file.exists()) {
addFile(file, AdtPlugin.readEmbeddedFile(TEMPLATES_DIRECTORY + ICON_MDPI), monitor);
}
@@ -919,22 +958,22 @@ public class NewProjectWizard extends Wizard implements INewWizard {
IFile file;
// high density
- file = project.getFile(RES_DIRECTORY + AndroidConstants.WS_SEP
- + DRAWABLE_HDPI_DIRECTORY + AndroidConstants.WS_SEP + PROJECT_ICON);
+ file = project.getFile(RES_DIRECTORY + AdtConstants.WS_SEP
+ + DRAWABLE_HDPI_DIRECTORY + AdtConstants.WS_SEP + PROJECT_ICON);
if (!file.exists()) {
addFile(file, AdtPlugin.readEmbeddedFile(TEMPLATES_DIRECTORY + ICON_HDPI), monitor);
}
// medium density
- file = project.getFile(RES_DIRECTORY + AndroidConstants.WS_SEP
- + DRAWABLE_MDPI_DIRECTORY + AndroidConstants.WS_SEP + PROJECT_ICON);
+ file = project.getFile(RES_DIRECTORY + AdtConstants.WS_SEP
+ + DRAWABLE_MDPI_DIRECTORY + AdtConstants.WS_SEP + PROJECT_ICON);
if (!file.exists()) {
addFile(file, AdtPlugin.readEmbeddedFile(TEMPLATES_DIRECTORY + ICON_MDPI), monitor);
}
// low density
- file = project.getFile(RES_DIRECTORY + AndroidConstants.WS_SEP
- + DRAWABLE_LDPI_DIRECTORY + AndroidConstants.WS_SEP + PROJECT_ICON);
+ file = project.getFile(RES_DIRECTORY + AdtConstants.WS_SEP
+ + DRAWABLE_LDPI_DIRECTORY + AdtConstants.WS_SEP + PROJECT_ICON);
if (!file.exists()) {
addFile(file, AdtPlugin.readEmbeddedFile(TEMPLATES_DIRECTORY + ICON_LDPI), monitor);
}
@@ -978,25 +1017,45 @@ public class NewProjectWizard extends Wizard implements INewWizard {
// The PARAM_ACTIVITY key will be absent if no activity should be created,
// in which case activityName will be null.
String activityName = (String) parameters.get(PARAM_ACTIVITY);
- Map<String, Object> java_activity_parameters = parameters;
+
+ Map<String, Object> java_activity_parameters = new HashMap<String, Object>(parameters);
+ java_activity_parameters.put(PARAM_IMPORT_RESOURCE_CLASS, ""); //$NON-NLS-1$
+
if (activityName != null) {
- if (activityName.indexOf('.') >= 0) {
- // There are package names in the activity name. Transform packageName to add
- // those sub packages and remove them from activityName.
- packageName += "." + activityName; //$NON-NLS-1$
- int pos = packageName.lastIndexOf('.');
- activityName = packageName.substring(pos + 1);
- packageName = packageName.substring(0, pos);
-
- // Also update the values used in the JAVA_FILE_TEMPLATE below
- // (but not the ones from the manifest so don't change the caller's dictionary)
- java_activity_parameters = new HashMap<String, Object>(parameters);
- java_activity_parameters.put(PARAM_PACKAGE, packageName);
- java_activity_parameters.put(PARAM_ACTIVITY, activityName);
+
+ String resourcePackageClass = null;
+
+ // An activity name can be of the form ".package.Class", ".Class" or FQDN.
+ // The initial dot is ignored, as it is always added later in the templates.
+ int lastDotIndex = activityName.lastIndexOf('.');
+
+ if (lastDotIndex != -1) {
+
+ // Resource class
+ if (lastDotIndex > 0) {
+ resourcePackageClass = packageName + "." + AdtConstants.FN_RESOURCE_BASE; //$NON-NLS-1$
+ }
+
+ // Package name
+ if (activityName.startsWith(".")) { //$NON-NLS-1$
+ packageName += activityName.substring(0, lastDotIndex);
+ } else {
+ packageName = activityName.substring(0, lastDotIndex);
+ }
+
+ // Activity Class name
+ activityName = activityName.substring(lastDotIndex + 1);
+ }
+
+ java_activity_parameters.put(PARAM_ACTIVITY, activityName);
+ java_activity_parameters.put(PARAM_PACKAGE, packageName);
+ if (resourcePackageClass != null) {
+ String importResourceClass = "\nimport " + resourcePackageClass + ";"; //$NON-NLS-1$ // $NON-NLS-2$
+ java_activity_parameters.put(PARAM_IMPORT_RESOURCE_CLASS, importResourceClass);
}
}
- String[] components = packageName.split(AndroidConstants.RE_DOT);
+ String[] components = packageName.split(AdtConstants.RE_DOT);
for (String component : components) {
pkgFolder = pkgFolder.getFolder(component);
if (!pkgFolder.exists()) {
@@ -1007,7 +1066,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
if (activityName != null) {
// create the main activity Java file
- String activityJava = activityName + AndroidConstants.DOT_JAVA;
+ String activityJava = activityName + AdtConstants.DOT_JAVA;
IFile file = pkgFolder.getFile(activityJava);
if (!file.exists()) {
copyFile(JAVA_ACTIVITY_TEMPLATE, file, java_activity_parameters, monitor);
@@ -1047,7 +1106,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
* Adds the given folder to the project's class path.
*
* @param javaProject The Java Project to update.
- * @param sourceFolder Template Parameters.
+ * @param sourceFolders Template Parameters.
* @param monitor An existing monitor.
* @throws JavaModelException if the classpath could not be set.
*/
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java
index fe24553..dfdd72e 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java
@@ -45,6 +45,7 @@ import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaConventions;
+import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.TextProcessor;
import org.eclipse.swt.SWT;
@@ -67,6 +68,8 @@ import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkingSet;
import java.io.File;
import java.net.URI;
@@ -86,6 +89,7 @@ import java.util.regex.Pattern;
* Note: this class is public so that it can be accessed from unit tests.
* It is however an internal class. Its API may change without notice.
* It should semantically be considered as a private final class.
+ * <p/>
* Do not derive from this class.
*/
public class NewTestProjectCreationPage extends WizardPage {
@@ -155,6 +159,7 @@ public class NewTestProjectCreationPage extends WizardPage {
private Label mTestTargetPackageLabel;
private String mLastExistingPackageName;
+ private WorkingSetGroup mWorkingSetGroup;
/**
@@ -165,6 +170,12 @@ public class NewTestProjectCreationPage extends WizardPage {
setPageComplete(false);
setTitle("New Android Test Project");
setDescription("Creates a new Android Test Project resource.");
+ mWorkingSetGroup= new WorkingSetGroup();
+ setWorkingSets(new IWorkingSet[0]);
+ }
+
+ public void init(IStructuredSelection selection, IWorkbenchPart activePart) {
+ setWorkingSets(WorkingSetHelper.getSelectedWorkingSet(selection, activePart));
}
// --- Getters used by NewProjectWizard ---
@@ -298,6 +309,7 @@ public class NewTestProjectCreationPage extends WizardPage {
createTestTargetGroup(composite);
createTargetGroup(composite);
createPropertiesGroup(composite);
+ createWorkingSetGroup(composite);
// Update state the first time
enableLocationWidgets();
@@ -693,6 +705,12 @@ public class NewTestProjectCreationPage extends WizardPage {
});
}
+ private void createWorkingSetGroup(final Composite composite) {
+ Composite group = mWorkingSetGroup.createControl(composite);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ mToggleComposites.add(group);
+ }
//--- Internal getters & setters ------------------
@@ -746,6 +764,7 @@ public class NewTestProjectCreationPage extends WizardPage {
*/
private void useMainProjectInformation() {
if (mInfo.isTestingMain() && mMainInfo != null) {
+ useMainWorkingSets();
String projName = String.format("%1$sTest", mMainInfo.getProjectName());
String appName = String.format("%1$sTest", mMainInfo.getApplicationName());
@@ -790,6 +809,17 @@ public class NewTestProjectCreationPage extends WizardPage {
}
}
+ private void useMainWorkingSets() {
+ IWorkingSet[] workingSets = mMainInfo.getSelectedWorkingSets();
+ if (workingSets != null) {
+ // getSelectedWorkingSets returns an empty list if the working set feature is disabled.
+ if (workingSets.length > 0) {
+ mWorkingSetGroup.setChecked(true);
+ }
+ mWorkingSetGroup.setWorkingSets(workingSets);
+ }
+ }
+
/**
* Callback invoked when the user edits the project text field.
*/
@@ -1342,4 +1372,24 @@ public class NewTestProjectCreationPage extends WizardPage {
return messageType;
}
+ /**
+ * Returns the working sets to which the new project should be added.
+ *
+ * @return the selected working sets to which the new project should be added
+ */
+ public IWorkingSet[] getWorkingSets() {
+ return mWorkingSetGroup.getSelectedWorkingSets();
+ }
+
+ /**
+ * Sets the working sets to which the new project should be added.
+ *
+ * @param workingSets the initial selected working sets
+ */
+ public void setWorkingSets(IWorkingSet[] workingSets) {
+ assert workingSets != null;
+ mWorkingSetGroup.setWorkingSets(workingSets);
+ }
+
+
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/WorkingSetGroup.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/WorkingSetGroup.java
new file mode 100644
index 0000000..fb33a08
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/WorkingSetGroup.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package com.android.ide.eclipse.adt.internal.wizards.newproject;
+
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.wizards.NewWizardMessages;
+import org.eclipse.jdt.internal.ui.workingsets.IWorkingSetIDs;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.ui.IWorkingSet;
+import org.eclipse.ui.dialogs.WorkingSetConfigurationBlock;
+
+/**
+ * Copied from
+ * org.eclipse.jdt.ui.wizards.NewJavaProjectWizardPageOne$WorkingSetGroup
+ *
+ * Creates the working set group with controls that allow
+ * the selection of working sets
+ */
+@SuppressWarnings("restriction")
+public class WorkingSetGroup {
+
+ private WorkingSetConfigurationBlock fWorkingSetBlock;
+ private Button mEnableButton;
+
+ public WorkingSetGroup() {
+ String[] workingSetIds = new String[] {
+ IWorkingSetIDs.JAVA, IWorkingSetIDs.RESOURCE
+ };
+ fWorkingSetBlock = new WorkingSetConfigurationBlock(workingSetIds, JavaPlugin.getDefault()
+ .getDialogSettings());
+ }
+
+ public Composite createControl(Composite composite) {
+ Group workingSetGroup = new Group(composite, SWT.NONE);
+ workingSetGroup.setFont(composite.getFont());
+ workingSetGroup.setText(NewWizardMessages.NewJavaProjectWizardPageOne_WorkingSets_group);
+ workingSetGroup.setLayout(new GridLayout(1, false));
+
+ fWorkingSetBlock.createContent(workingSetGroup);
+
+ // WorkingSetGroup is implemented in such a way that the checkbox it contains
+ // can only be programmatically set if there's an existing working set associated
+ // *before* we construct the control. However the control is created when the
+ // wizard is opened, not when the page is first shown.
+ //
+ // One choice is to duplicate the class in our project.
+ // Or find the checkbox we want and trigger it manually.
+ mEnableButton = findCheckbox(workingSetGroup);
+
+ return workingSetGroup;
+ }
+
+ public void setWorkingSets(IWorkingSet[] workingSets) {
+ fWorkingSetBlock.setWorkingSets(workingSets);
+ }
+
+ public IWorkingSet[] getSelectedWorkingSets() {
+ try {
+ return fWorkingSetBlock.getSelectedWorkingSets();
+ } catch (Throwable t) {
+ // Test scenarios; no UI is created, which the fWorkingSetBlock assumes
+ // (it dereferences the enabledButton)
+ return new IWorkingSet[0];
+ }
+ }
+
+ public boolean isChecked() {
+ return mEnableButton == null ? false : mEnableButton.getSelection();
+ }
+
+ public void setChecked(boolean state) {
+ if (mEnableButton != null) {
+ mEnableButton.setSelection(state);
+ }
+ }
+
+ /**
+ * Finds the first button of style Checkbox in the given parent composite.
+ * Returns null if not found.
+ */
+ private Button findCheckbox(Composite parent) {
+ for (Control control : parent.getChildren()) {
+ if (control instanceof Button && (control.getStyle() & SWT.CHECK) == SWT.CHECK) {
+ return (Button) control;
+ } else if (control instanceof Composite) {
+ Button found = findCheckbox((Composite) control);
+ if (found != null) {
+ return found;
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/WorkingSetHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/WorkingSetHelper.java
new file mode 100755
index 0000000..428bfd3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/WorkingSetHelper.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package com.android.ide.eclipse.adt.internal.wizards.newproject;
+
+import org.eclipse.jdt.internal.ui.packageview.PackageExplorerPart;
+import org.eclipse.jdt.internal.ui.workingsets.IWorkingSetIDs;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkingSet;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class contains a helper method to deal with working sets.
+ * <p/>
+ * Copied from org.eclipse.jdt.ui.wizards.NewJavaProjectWizardPageOne
+ */
+@SuppressWarnings("restriction")
+public final class WorkingSetHelper {
+
+ private static final IWorkingSet[] EMPTY_WORKING_SET_ARRAY = new IWorkingSet[0];
+
+ /** This class is never instantiated. */
+ private WorkingSetHelper() {
+ }
+
+ public static IWorkingSet[] getSelectedWorkingSet(IStructuredSelection selection,
+ IWorkbenchPart activePart) {
+ IWorkingSet[] selected= getSelectedWorkingSet(selection);
+ if (selected != null && selected.length > 0) {
+ for (int i= 0; i < selected.length; i++) {
+ if (!isValidWorkingSet(selected[i]))
+ return EMPTY_WORKING_SET_ARRAY;
+ }
+ return selected;
+ }
+
+ if (!(activePart instanceof PackageExplorerPart))
+ return EMPTY_WORKING_SET_ARRAY;
+
+ PackageExplorerPart explorerPart= (PackageExplorerPart) activePart;
+ if (explorerPart.getRootMode() == PackageExplorerPart.PROJECTS_AS_ROOTS) {
+ //Get active filter
+ IWorkingSet filterWorkingSet= explorerPart.getFilterWorkingSet();
+ if (filterWorkingSet == null)
+ return EMPTY_WORKING_SET_ARRAY;
+
+ if (!isValidWorkingSet(filterWorkingSet))
+ return EMPTY_WORKING_SET_ARRAY;
+
+ return new IWorkingSet[] {filterWorkingSet};
+ } else {
+ //If we have been gone into a working set return the working set
+ Object input= explorerPart.getViewPartInput();
+ if (!(input instanceof IWorkingSet))
+ return EMPTY_WORKING_SET_ARRAY;
+
+ IWorkingSet workingSet= (IWorkingSet)input;
+ if (!isValidWorkingSet(workingSet))
+ return EMPTY_WORKING_SET_ARRAY;
+
+ return new IWorkingSet[] {workingSet};
+ }
+ }
+
+ private static IWorkingSet[] getSelectedWorkingSet(IStructuredSelection selection) {
+ if (!(selection instanceof ITreeSelection))
+ return EMPTY_WORKING_SET_ARRAY;
+
+ ITreeSelection treeSelection= (ITreeSelection) selection;
+ if (treeSelection.isEmpty())
+ return EMPTY_WORKING_SET_ARRAY;
+
+ List<?> elements = treeSelection.toList();
+ if (elements.size() == 1) {
+ Object element= elements.get(0);
+ TreePath[] paths= treeSelection.getPathsFor(element);
+ if (paths.length != 1)
+ return EMPTY_WORKING_SET_ARRAY;
+
+ TreePath path= paths[0];
+ if (path.getSegmentCount() == 0)
+ return EMPTY_WORKING_SET_ARRAY;
+
+ Object candidate= path.getSegment(0);
+ if (!(candidate instanceof IWorkingSet))
+ return EMPTY_WORKING_SET_ARRAY;
+
+ IWorkingSet workingSetCandidate= (IWorkingSet) candidate;
+ if (isValidWorkingSet(workingSetCandidate))
+ return new IWorkingSet[] { workingSetCandidate };
+
+ return EMPTY_WORKING_SET_ARRAY;
+ }
+
+ ArrayList<Object> result = new ArrayList<Object>();
+ for (Iterator<?> iterator = elements.iterator(); iterator.hasNext();) {
+ Object element= iterator.next();
+ if (element instanceof IWorkingSet && isValidWorkingSet((IWorkingSet) element)) {
+ result.add(element);
+ }
+ }
+ return result.toArray(new IWorkingSet[result.size()]);
+ }
+
+
+ private static boolean isValidWorkingSet(IWorkingSet workingSet) {
+ String id= workingSet.getId();
+ if (!IWorkingSetIDs.JAVA.equals(id) && !IWorkingSetIDs.RESOURCE.equals(id))
+ return false;
+
+ if (workingSet.isAggregateWorkingSet())
+ return false;
+
+ return true;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java
index 03aed88..c4f86a6 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java
@@ -17,8 +17,17 @@
package com.android.ide.eclipse.adt.internal.wizards.newxmlfile;
+import static com.android.ide.common.layout.LayoutConstants.HORIZONTAL_SCROLL_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.SCROLL_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_FILL_PARENT;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_MATCH_PARENT;
+
+import com.android.AndroidConstants;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
@@ -27,15 +36,14 @@ import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.Resour
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper.IProjectFilter;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
+import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.SelectorMode;
+import com.android.resources.ResourceFolderType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.android.util.Pair;
@@ -65,6 +73,11 @@ import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.FileEditorInput;
import java.util.ArrayList;
import java.util.Collections;
@@ -203,6 +216,18 @@ class NewXmlFileCreationPage extends WizardPage {
}
/**
+ * When not null, represents an extra string that should be written inside
+ * the element when constructed
+ *
+ * @param project the project to get the child content for
+ * @param root the chosen root element
+ * @return a string to be written inside the root element, or null if nothing
+ */
+ String getChild(IProject project, String root) {
+ return null;
+ }
+
+ /**
* The minimum API level required by the current SDK target to support this feature.
*
* @return the minimum API level
@@ -232,17 +257,31 @@ class NewXmlFileCreationPage extends WizardPage {
@Override
String getDefaultAttrs(IProject project) {
Sdk currentSdk = Sdk.getCurrent();
+ String fill = VALUE_FILL_PARENT;
if (currentSdk != null) {
IAndroidTarget target = currentSdk.getTarget(project);
// fill_parent was renamed match_parent in API level 8
if (target != null && target.getVersion().getApiLevel() >= 8) {
- return "android:layout_width=\"match_parent\"\n" //$NON-NLS-1$
- + "android:layout_height=\"match_parent\""; //$NON-NLS-1$
+ fill = VALUE_MATCH_PARENT;
}
}
- return "android:layout_width=\"fill_parent\"\n" //$NON-NLS-1$
- + "android:layout_height=\"fill_parent\""; //$NON-NLS-1$
+ return String.format(
+ "android:orientation=\"vertical\"\n" //$NON-NLS-1$
+ + "android:layout_width=\"%1$s\"\n" //$NON-NLS-1$
+ + "android:layout_height=\"%1$s\"", //$NON-NLS-1$
+ fill, fill);
+ }
+
+ @Override
+ String getChild(IProject project, String root) {
+ // Create vertical linear layouts inside new scroll views
+ if (SCROLL_VIEW.equals(root) || HORIZONTAL_SCROLL_VIEW.equals(root)) {
+ return " <LinearLayout " //$NON-NLS-1$
+ + getDefaultAttrs(project).replace('\n', ' ')
+ + "></LinearLayout>\n"; //$NON-NLS-1$
+ }
+ return null;
}
},
new TypeInfo("Values", // UI name
@@ -254,6 +293,15 @@ class NewXmlFileCreationPage extends WizardPage {
null, // default attributes
1 // target API level
),
+ new TypeInfo("Drawable", // UI name
+ "An XML file that describes a drawable.", // tooltip
+ ResourceFolderType.DRAWABLE, // folder type
+ AndroidTargetData.DESCRIPTOR_DRAWABLE, // root seed
+ null, // default root
+ SdkConstants.NS_RESOURCES, // xmlns
+ null, // default attributes
+ 1 // target API level
+ ),
new TypeInfo("Menu", // UI name
"An XML file that describes an menu.", // tooltip
ResourceFolderType.MENU, // folder type
@@ -263,6 +311,33 @@ class NewXmlFileCreationPage extends WizardPage {
null, // default attributes
1 // target API level
),
+ new TypeInfo("Color List", // UI name
+ "An XML file that describes a color state list.", // tooltip
+ ResourceFolderType.COLOR, // folder type
+ AndroidTargetData.DESCRIPTOR_COLOR, // root seed
+ null, // default root
+ SdkConstants.NS_RESOURCES, // xmlns
+ null, // default attributes
+ 1 // target API level
+ ),
+ new TypeInfo("Animator", // UI name
+ "An XML file that describes an animator.", // tooltip
+ ResourceFolderType.ANIMATOR, // folder type
+ AndroidTargetData.DESCRIPTOR_ANIMATOR, // root seed
+ "set", // default root
+ SdkConstants.NS_RESOURCES, // xmlns
+ null, // default attributes
+ 11 // target API level
+ ),
+ new TypeInfo("Animation", // UI name
+ "An XML file that describes an animation.", // tooltip
+ ResourceFolderType.ANIM, // folder type
+ AndroidTargetData.DESCRIPTOR_ANIM, // root seed
+ "set", // default root
+ null, // xmlns
+ null, // default attributes
+ 1 // target API level
+ ),
new TypeInfo("AppWidget Provider", // UI name
"An XML file that describes a widget provider.", // tooltip
ResourceFolderType.XML, // folder type
@@ -290,31 +365,15 @@ class NewXmlFileCreationPage extends WizardPage {
null, // default attributes
1 // target API level
),
- new TypeInfo("Animation", // UI name
- "An XML file that describes an animation.", // tooltip
- ResourceFolderType.ANIM, // folder type
- // TODO reuse constants if we ever make an editor with descriptors for animations
- new String[] { // root seed
- "set", //$NON-NLS-1$
- "alpha", //$NON-NLS-1$
- "scale", //$NON-NLS-1$
- "translate", //$NON-NLS-1$
- "rotate" //$NON-NLS-1$
- },
- "set", //$NON-NLS-1$ // default root
- null, // xmlns
- null, // default attributes
- 1 // target API level
- ),
};
/** Number of columns in the grid layout */
final static int NUM_COL = 4;
/** Absolute destination folder root, e.g. "/res/" */
- private static final String RES_FOLDER_ABS = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
+ private static final String RES_FOLDER_ABS = AdtConstants.WS_RESOURCES + AdtConstants.WS_SEP;
/** Relative destination folder root, e.g. "res/" */
- private static final String RES_FOLDER_REL = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
+ private static final String RES_FOLDER_REL = SdkConstants.FD_RESOURCES + AdtConstants.WS_SEP;
private IProject mProject;
private Text mProjectTextField;
@@ -432,7 +491,7 @@ class NewXmlFileCreationPage extends WizardPage {
} else {
fileName = mFileNameTextField.getText().trim();
if (fileName.length() > 0 && fileName.indexOf('.') == -1) {
- fileName = fileName + AndroidConstants.DOT_XML;
+ fileName = fileName + AdtConstants.DOT_XML;
}
}
@@ -642,7 +701,7 @@ class NewXmlFileCreationPage extends WizardPage {
};
int n = sTypes.length;
- int num_lines = (n + NUM_COL/2) / NUM_COL;
+ int num_lines = (n + (NUM_COL - 1)) / NUM_COL;
for (int line = 0, k = 0; line < num_lines; line++) {
for (int i = 0; i < NUM_COL; i++, k++) {
if (k < n) {
@@ -754,7 +813,7 @@ class NewXmlFileCreationPage extends WizardPage {
// Is this an Android project?
try {
- if (project == null || !project.hasNature(AndroidConstants.NATURE_DEFAULT)) {
+ if (project == null || !project.hasNature(AdtConstants.NATURE_DEFAULT)) {
continue;
}
} catch (CoreException e) {
@@ -804,6 +863,25 @@ class NewXmlFileCreationPage extends WizardPage {
}
if (targetProject == null) {
+ // Try to figure out the project from the active editor
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window != null) {
+ IWorkbenchPage page = window.getActivePage();
+ if (page != null) {
+ IEditorPart activeEditor = page.getActiveEditor();
+ if (activeEditor instanceof AndroidXmlEditor) {
+ Object input = ((AndroidXmlEditor) activeEditor).getEditorInput();
+ if (input instanceof FileEditorInput) {
+ FileEditorInput fileInput = (FileEditorInput) input;
+ targetScore = 1;
+ targetProject = fileInput.getFile().getProject();
+ }
+ }
+ }
+ }
+ }
+
+ if (targetProject == null) {
// If we didn't find a default project based on the selection, check how many
// open Android projects we can find in the current workspace. If there's only
// one, we'll just select it by default.
@@ -1017,12 +1095,12 @@ class NewXmlFileCreationPage extends WizardPage {
if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
- int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR);
+ int pos = wsFolderPath.indexOf(AdtConstants.WS_SEP_CHAR);
if (pos >= 0) {
wsFolderPath = wsFolderPath.substring(0, pos);
}
- String[] folderSegments = wsFolderPath.split(FolderConfiguration.QUALIFIER_SEP);
+ String[] folderSegments = wsFolderPath.split(AndroidConstants.RES_QUALIFIER_SEP);
if (folderSegments.length > 0) {
String folderName = folderSegments[0];
@@ -1040,19 +1118,6 @@ class NewXmlFileCreationPage extends WizardPage {
}
}
- // For now, treat a selection of /res/animator as /res/anim/,
- // though we need to handle this better
- // TODO: Properly support ANIMATOR templates!
- if (!selected && folderName.equals(SdkConstants.FD_ANIMATOR)) {
- for (TypeInfo type : sTypes) {
- if (type.getResFolderType() == ResourceFolderType.ANIM) {
- matches.add(type);
- selected |= type.getWidget().getSelection();
- break;
- }
- }
- }
-
if (matches.size() == 1) {
// If there's only one match, select it if it's not already selected
if (!selected) {
@@ -1255,12 +1320,6 @@ class NewXmlFileCreationPage extends WizardPage {
error = "Please select an Android project.";
}
- // -- validate filename
- if (error == null) {
- String fileName = getFileName();
- error = ResourceNameValidator.create(true).isValid(fileName);
- }
-
// -- validate type
if (error == null) {
TypeInfo type = getSelectedType();
@@ -1270,6 +1329,13 @@ class NewXmlFileCreationPage extends WizardPage {
}
}
+ // -- validate filename
+ if (error == null) {
+ String fileName = getFileName();
+ ResourceFolderType folderType = getSelectedType().getResFolderType();
+ error = ResourceNameValidator.create(true, folderType).isValid(fileName);
+ }
+
// -- validate type API level
if (error == null) {
IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
@@ -1328,4 +1394,20 @@ class NewXmlFileCreationPage extends WizardPage {
}
}
+ /**
+ * Returns the {@link TypeInfo} for the given {@link ResourceFolderType}, or null if
+ * not found
+ *
+ * @param folderType the {@link ResourceFolderType} to look for
+ * @return the corresponding {@link TypeInfo}
+ */
+ static TypeInfo getTypeInfo(ResourceFolderType folderType) {
+ for (TypeInfo typeInfo : sTypes) {
+ if (typeInfo.getResFolderType() == folderType) {
+ return typeInfo;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
index a87400b..442568f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
@@ -21,23 +21,24 @@ package com.android.ide.eclipse.adt.internal.wizards.newxmlfile;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo;
+import com.android.resources.ResourceFolderType;
+import com.android.util.Pair;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.ide.IDE;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@@ -51,8 +52,9 @@ import java.io.UnsupportedEncodingException;
* the resource folder, resource type and file name. It then creates the XML file.
*/
public class NewXmlFileWizard extends Wizard implements INewWizard {
+ public static final String XML_HEADER_LINE = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$
- private static final String PROJECT_LOGO_LARGE = "android_large"; //$NON-NLS-1$
+ private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$
protected static final String MAIN_PAGE_NAME = "newAndroidXmlFilePage"; //$NON-NLS-1$
@@ -105,22 +107,18 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
*/
@Override
public boolean performFinish() {
- IFile file = createXmlFile();
- if (file == null) {
+ Pair<IFile, IRegion> created = createXmlFile();
+ if (created == null) {
return false;
} else {
- // Open the file in an editor
- IWorkbenchWindow win = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
- if (win != null) {
- IWorkbenchPage page = win.getActivePage();
- if (page != null) {
- try {
- IDE.openEditor(page, file);
- } catch (PartInitException e) {
- AdtPlugin.log(e, "Failed to create %1$s: missing type", //$NON-NLS-1$
- file.getFullPath().toString());
- }
- }
+ IFile file = created.getFirst();
+
+ // Open the file
+ try {
+ AdtPlugin.openFile(file, null, false /* showEditorTab */);
+ } catch (PartInitException e) {
+ AdtPlugin.log(e, "Failed to create %1$s: missing type", //$NON-NLS-1$
+ file.getFullPath().toString());
}
return true;
}
@@ -128,25 +126,12 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
// -- Custom Methods --
- private IFile createXmlFile() {
+ private Pair<IFile, IRegion> createXmlFile() {
IFile file = mMainPage.getDestinationFile();
- String name = file.getFullPath().toString();
- boolean need_delete = false;
-
- if (file.exists()) {
- if (!AdtPlugin.displayPrompt("New Android XML File",
- String.format("Do you want to overwrite the file %1$s ?", name))) {
- // abort if user selects cancel.
- return null;
- }
- need_delete = true;
- } else {
- createWsParentDirectory(file.getParent());
- }
-
TypeInfo type = mMainPage.getSelectedType();
if (type == null) {
// this is not expected to happen
+ String name = file.getFullPath().toString();
AdtPlugin.log(IStatus.ERROR, "Failed to create %1$s: missing type", name); //$NON-NLS-1$
return null;
}
@@ -159,20 +144,52 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
return null;
}
- StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); //$NON-NLS-1$
+ String attrs = type.getDefaultAttrs(mMainPage.getProject());
+
+ String child = type.getChild(mMainPage.getProject(), root);
+ return createXmlFile(file, xmlns, root, attrs, child);
+ }
+
+ /** Creates a new file using the given root element, namespace and root attributes */
+ private static Pair<IFile, IRegion> createXmlFile(IFile file, String xmlns,
+ String root, String rootAttributes, String child) {
+ String name = file.getFullPath().toString();
+ boolean need_delete = false;
+
+ if (file.exists()) {
+ if (!AdtPlugin.displayPrompt("New Android XML File",
+ String.format("Do you want to overwrite the file %1$s ?", name))) {
+ // abort if user selects cancel.
+ return null;
+ }
+ need_delete = true;
+ } else {
+ createWsParentDirectory(file.getParent());
+ }
+
+ StringBuilder sb = new StringBuilder(XML_HEADER_LINE);
sb.append('<').append(root);
if (xmlns != null) {
sb.append('\n').append(" xmlns:android=\"").append(xmlns).append("\""); //$NON-NLS-1$ //$NON-NLS-2$
}
- String attrs = type.getDefaultAttrs(mMainPage.getProject());
- if (attrs != null) {
+ if (rootAttributes != null) {
sb.append("\n "); //$NON-NLS-1$
- sb.append(attrs.replace("\n", "\n ")); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append(rootAttributes.replace("\n", "\n ")); //$NON-NLS-1$ //$NON-NLS-2$
}
sb.append(">\n"); //$NON-NLS-1$
+
+ if (child != null) {
+ sb.append(child);
+ }
+
+ // The insertion line
+ sb.append(" "); //$NON-NLS-1$
+ int caretOffset = sb.length();
+ sb.append("\n"); //$NON-NLS-1$
+
sb.append("</").append(root).append(">\n"); //$NON-NLS-1$ //$NON-NLS-2$
String result = sb.toString();
@@ -184,7 +201,8 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
file.delete(IResource.KEEP_HISTORY | IResource.FORCE, null /*monitor*/);
}
file.create(stream, true /*force*/, null /*progress*/);
- return file;
+ IRegion region = new Region(caretOffset, 0);
+ return Pair.of(file, region);
} catch (UnsupportedEncodingException e) {
error = e.getMessage();
} catch (CoreException e) {
@@ -196,7 +214,40 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
return null;
}
- private boolean createWsParentDirectory(IContainer wsPath) {
+ /**
+ * Returns true if the New XML Wizard can create new files of the given
+ * {@link ResourceFolderType}
+ *
+ * @param folderType the folder type to create a file for
+ * @return true if this wizard can create new files for the given folder type
+ */
+ public static boolean canCreateXmlFile(ResourceFolderType folderType) {
+ TypeInfo typeInfo = NewXmlFileCreationPage.getTypeInfo(folderType);
+ return typeInfo != null && (typeInfo.getDefaultRoot() != null ||
+ typeInfo.getRootSeed() instanceof String);
+ }
+
+ /**
+ * Creates a new XML file using the template according to the given folder type
+ *
+ * @param project the project to create the file in
+ * @param file the file to be created
+ * @param folderType the type of folder to look up a template for
+ * @return the created file
+ */
+ public static Pair<IFile, IRegion> createXmlFile(IProject project, IFile file,
+ ResourceFolderType folderType) {
+ TypeInfo type = NewXmlFileCreationPage.getTypeInfo(folderType);
+ String xmlns = type.getXmlns();
+ String root = type.getDefaultRoot();
+ if (root == null) {
+ root = type.getRootSeed().toString();
+ }
+ String attrs = type.getDefaultAttrs(project);
+ return createXmlFile(file, xmlns, root, attrs, null);
+ }
+
+ private static boolean createWsParentDirectory(IContainer wsPath) {
if (wsPath.getType() == IResource.FOLDER) {
if (wsPath == null || wsPath.exists()) {
return true;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFileWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFileWrapper.java
index a444cdc..b4e7a3f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFileWrapper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFileWrapper.java
@@ -16,9 +16,9 @@
package com.android.ide.eclipse.adt.io;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.IAbstractFolder;
-import com.android.sdklib.io.StreamException;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.io.StreamException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -97,6 +97,10 @@ public class IFileWrapper implements IAbstractFile {
return mFile;
}
+ public long getModificationStamp() {
+ return mFile.getModificationStamp();
+ }
+
@Override
public boolean equals(Object obj) {
if (obj instanceof IFileWrapper) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFolderWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFolderWrapper.java
index 3c00485..85106c2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFolderWrapper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/io/IFolderWrapper.java
@@ -16,9 +16,9 @@
package com.android.ide.eclipse.adt.io;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.IAbstractFolder;
-import com.android.sdklib.io.IAbstractResource;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.io.IAbstractResource;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/activity.template b/eclipse/plugins/com.android.ide.eclipse.adt/templates/activity.template
index e91d602..ee17fb8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/activity.template
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/templates/activity.template
@@ -1,7 +1,7 @@
- <activity android:name=".ACTIVITY_NAME"
+ <activity android:name="ACTIVITY_NAME"
android:label="APPLICATION_NAME">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
INTENT_FILTERS
</intent-filter>
- </activity> \ No newline at end of file
+ </activity>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/java_file.template b/eclipse/plugins/com.android.ide.eclipse.adt/templates/java_file.template
index 173ff96..e40e77e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/java_file.template
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/templates/java_file.template
@@ -1,5 +1,5 @@
package PACKAGE;
-
+IMPORT_RESOURCE_CLASS
import android.app.Activity;
import android.os.Bundle;
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/.settings/org.eclipse.jdt.core.prefs b/eclipse/plugins/com.android.ide.eclipse.ddms/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF
index 36bd69c..1b39d70 100644
--- a/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF
@@ -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: 10.0.0.qualifier
+Bundle-Version: 11.0.0.qualifier
Bundle-Activator: com.android.ide.eclipse.ddms.DdmsPlugin
Bundle-Vendor: The Android Open Source Project
Bundle-Localization: plugin
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/about.ini b/eclipse/plugins/com.android.ide.eclipse.ddms/about.ini
index 560e475..3395379 100644
--- a/eclipse/plugins/com.android.ide.eclipse.ddms/about.ini
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/about.ini
@@ -1,2 +1,2 @@
aboutText=%blurb
-featureImage=icons/android_32x32.png \ No newline at end of file
+featureImage=icons/ddms-32.png \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/icons/android_32x32.png b/eclipse/plugins/com.android.ide.eclipse.ddms/icons/android_32x32.png
deleted file mode 100644
index a382aad..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.ddms/icons/android_32x32.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/icons/ddms-16.png b/eclipse/plugins/com.android.ide.eclipse.ddms/icons/ddms-16.png
new file mode 100644
index 0000000..f40e720
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/icons/ddms-16.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/icons/ddms-32.png b/eclipse/plugins/com.android.ide.eclipse.ddms/icons/ddms-32.png
new file mode 100644
index 0000000..d54ab7f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/icons/ddms-32.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator-16.png b/eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator-16.png
new file mode 100644
index 0000000..7554b24
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator-16.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator.png b/eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator.png
deleted file mode 100644
index a718042..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.ddms/plugin.xml
index 9bc5a0c..cc47384 100644
--- a/eclipse/plugins/com.android.ide.eclipse.ddms/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/plugin.xml
@@ -60,7 +60,7 @@
allowMultiple="false"
category="com.android.ide.eclipse.ddms.views.category"
class="com.android.ide.eclipse.ddms.views.EmulatorControlView"
- icon="icons/emulator.png"
+ icon="icons/emulator-16.png"
id="com.android.ide.eclipse.ddms.views.EmulatorControlView"
name="Emulator Control"/>
<view
@@ -75,7 +75,7 @@
point="org.eclipse.ui.perspectives">
<perspective
class="com.android.ide.eclipse.ddms.Perspective"
- icon="icons/android.png"
+ icon="icons/ddms-16.png"
id="com.android.ide.eclipse.ddms.Perspective"
name="DDMS"/>
</extension>
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/FileExplorerView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/FileExplorerView.java
index b3e08a2..57e5370 100644
--- a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/FileExplorerView.java
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/FileExplorerView.java
@@ -110,8 +110,18 @@ public class FileExplorerView extends ViewPart implements ISelectionListener {
deleteAction.setImageDescriptor(loader.loadDescriptor("delete.png")); //$NON-NLS-1$
deleteAction.setEnabled(false);
+ CommonAction createNewFolderAction = new CommonAction("New Folder") {
+ @Override
+ public void run() {
+ mExplorer.createNewFolderInSelection();
+ }
+ };
+ createNewFolderAction.setToolTipText("New Folder");
+ createNewFolderAction.setImageDescriptor(loader.loadDescriptor("add.png")); //$NON-NLS-1$
+ createNewFolderAction.setEnabled(false);
+
// set up the actions in the explorer
- mExplorer.setActions(pushAction, pullAction, deleteAction);
+ mExplorer.setActions(pushAction, pullAction, deleteAction, createNewFolderAction);
// and in the ui
IActionBars actionBars = getViewSite().getActionBars();
@@ -122,11 +132,15 @@ public class FileExplorerView extends ViewPart implements ISelectionListener {
menuManager.add(pushAction);
menuManager.add(new Separator());
menuManager.add(deleteAction);
+ menuManager.add(new Separator());
+ menuManager.add(createNewFolderAction);
toolBarManager.add(pullAction);
toolBarManager.add(pushAction);
toolBarManager.add(new Separator());
toolBarManager.add(deleteAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(createNewFolderAction);
mExplorer.createPanel(parent);
diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/.settings/org.eclipse.jdt.core.prefs b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/META-INF/MANIFEST.MF
index 75e828b..d494e48 100644
--- a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Hierarchy Viewer
Bundle-SymbolicName: com.android.ide.eclipse.hierarchyviewer;singleton:=true
-Bundle-Version: 10.0.0.qualifier
+Bundle-Version: 11.0.0.qualifier
Bundle-Activator: com.android.ide.eclipse.hierarchyviewer.HierarchyViewerPlugin
Bundle-Vendor: The Android Open Source Project
Bundle-Localization: plugin
diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/about.ini b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/about.ini
index 560e475..18f6ab0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/about.ini
+++ b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/about.ini
@@ -1,2 +1,2 @@
aboutText=%blurb
-featureImage=icons/android_32x32.png \ No newline at end of file
+featureImage=icons/hierarchyviewer-32.png \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/android_32x32.png b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/android_32x32.png
deleted file mode 100644
index a382aad..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/android_32x32.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/hierarchyviewer-16.png b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/hierarchyviewer-16.png
new file mode 100644
index 0000000..02073d4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/hierarchyviewer-16.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/hierarchyviewer-32.png b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/hierarchyviewer-32.png
new file mode 100644
index 0000000..fa21c24
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/icons/hierarchyviewer-32.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/plugin.xml
index b8a6762..a4c2994 100644
--- a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/plugin.xml
@@ -77,7 +77,7 @@
name="Pixel Perfect"/>
<perspective
class="com.android.ide.eclipse.hierarchyviewer.TreeViewPerspective"
- icon="icons/tree-view.png"
+ icon="icons/hierarchyviewer-16.png"
id="com.android.ide.eclipse.hierarchyviewer.TreeViewPerspective"
name="Hierarchy View"/>
</extension>
diff --git a/eclipse/plugins/com.android.ide.eclipse.pdt/.settings/org.eclipse.jdt.core.prefs b/eclipse/plugins/com.android.ide.eclipse.pdt/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.pdt/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/eclipse/plugins/com.android.ide.eclipse.pdt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.pdt/META-INF/MANIFEST.MF
index 15f9510..3781ddc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.pdt/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.pdt/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Pdt
Bundle-SymbolicName: com.android.ide.eclipse.pdt;singleton:=true
-Bundle-Version: 10.0.0.qualifier
+Bundle-Version: 11.0.0.qualifier
Bundle-Vendor: The Android Open Source Project
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/.settings/org.eclipse.jdt.core.prefs b/eclipse/plugins/com.android.ide.eclipse.tests/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
index 9d4285c..b6fe951 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
@@ -2,9 +2,9 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Android Plugin Tests
Bundle-SymbolicName: com.android.ide.eclipse.tests
-Bundle-Version: 10.0.0.qualifier
+Bundle-Version: 11.0.0.qualifier
Bundle-Vendor: The Android Open Source Project
-Fragment-Host: com.android.ide.eclipse.adt;bundle-version="10.0.0"
+Fragment-Host: com.android.ide.eclipse.adt;bundle-version="11.0.0"
Require-Bundle: org.junit
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-ClassPath: kxml2-2.3.0.jar,
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptParserTest.java
new file mode 100644
index 0000000..af4e2b7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptParserTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2011 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.build;
+
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+public class AaptParserTest extends AdtProjectTest {
+
+ public void testBasic() throws Exception {
+ // Test the "at 'property' with value 'value' range matching included with most aapt errors
+ checkRanges("quickfix1.xml", "res/layout/quickfix1.xml",
+ "quickfix1.xml:7: error: Error: No resource found that matches the given name (at"
+ + " 'text' with value '@string/firststring').",
+ "android:text=\"^@string/firststring\"",
+ "android:text=\"@string/firststring^\"");
+ }
+
+ public void testRange1() throws Exception {
+ // Check that when the actual aapt error occurs on a line later than the original error
+ // line, the forward search which looks for a value match does not stop on an
+ // earlier line that happens to have the same value prefix
+ checkRanges("aapterror1.xml", "res/layout/aapterror1.xml",
+ "aapterror1.xml:5: error: Error: Integer types not allowed (at "
+ + "'layout_marginBottom' with value '50').",
+ "marginBottom=\"^50\"", "marginBottom=\"50^\"");
+ }
+
+ public void testRange2() throws Exception {
+ // Check that when we have a duplicate resource error, we highlight both the original
+ // property and the original definition.
+ // This tests the second, duplicate declaration ration.
+ checkRanges("aapterror2.xml", "res/values/aapterror2.xml",
+ "aapterror2.xml:7: error: Resource entry repeatedStyle1 already has bag item "
+ + "android:gravity.",
+ "<item name=\"^android:gravity\">bottom</item>",
+ "<item name=\"android:gravity^\">bottom</item>");
+ }
+
+ public void testRange3() throws Exception {
+ // Check that when we have a duplicate resource error, we highlight both the original
+ // property and the original definition.
+ // This tests the original definition. Note that we don't have enough position info
+ // so we simply highlight the whitespace portion of the line.
+ checkRanges("aapterror2.xml", "res/values/aapterror2.xml",
+ "aapterror2.xml:4: Originally defined here.",
+ "^<item name=\"android:gravity\">left</item>",
+ "<item name=\"android:gravity\">left</item>^");
+ }
+
+ public void testRange4() throws Exception {
+ // Check for aapt error which occurs when the attribute name in an item style declaration
+ // is nonexistent
+ checkRanges("aapterror3.xml", "res/values/aapterror3.xml",
+ "aapterror3.xml:4: error: Error: No resource found that matches the given name: "
+ + "attr 'nonexistent'.",
+ "<item name=\"^nonexistent\">5</item>",
+ "<item name=\"nonexistent^\">5</item>");
+ }
+
+ public void testRange5() throws Exception {
+ // Test missing resource name
+ checkRanges("aapterror4.xml", "res/values/aapterror4.xml",
+ "aapterror4.xml:3: error: A 'name' attribute is required for <style>",
+ "<^style>",
+ "<style^>");
+ }
+
+ public void testRange6() throws Exception {
+ checkRanges("aapterror4.xml", "res/values/aapterror4.xml",
+ "aapterror4.xml:6: error: A 'type' attribute is required for <item>",
+ "<^item></item>",
+ "<item^></item>");
+ }
+
+ public void testRange7() throws Exception {
+ // Test missing resource name
+ checkRanges("aapterror4.xml", "res/values/aapterror4.xml",
+ "aapterror4.xml:6: error: A 'name' attribute is required for <item>",
+ "<^item></item>",
+ "<item^></item>");
+ }
+
+ // This test is disabled because I can't find a useful scenario for handling this error
+ // message. When this error occurs, we will also get a warning on a missing attribute, and
+ // that warning already underlines the element name.
+ //public void testRange8() throws Exception {
+ // // Test missing resource name
+ // checkRanges("aapterror4.xml", "res/values/aapterror4.xml",
+ // "aapterror4.xml:4: error: Error: Resource id cannot be an empty string: attr ''.",
+ // " ^<item />",
+ // " <item />^");
+ //}
+
+ public void testRange9() throws Exception {
+ // Test missing resource name
+ checkRanges("aapterror5.xml", "res/values/aapterror5.xml",
+ "aapterror5.xml:4: error: Error: String types not allowed (at "
+ + "'android:layout_width' with value '').",
+ " <item name=\"^android:layout_width\"></item>",
+ " <item name=\"android:layout_width^\"></item>");
+ }
+
+ public void testRange10() throws Exception {
+ // Test missing resource name
+ checkRanges("aapterror6.xml", "res/layout/aapterror6.xml",
+ "aapterror6.xml:5: error: Error: String types not allowed (at 'layout_marginTop'"
+ + " with value '').",
+ "android:layout_marginTop=^\"\"",
+ "android:layout_marginTop=\"\"^");
+ }
+
+ public void testRange11() throws Exception {
+ // Test missing resource name
+ checkRanges("aapterror6.xml", "res/layout/aapterror6.xml",
+ "aapterror1.xml:5: error: Error: String types not allowed (at 'layout_marginLeft'"
+ + " with value '').",
+ "android:layout_marginLeft=^''",
+ "android:layout_marginLeft=''^");
+ }
+
+ public void testRange12() throws Exception {
+ // Test missing resource name
+ checkRanges("aapterror7.xml", "res/layout/aapterror7.xml",
+ "aapterror7.xml:5: error: Error: String types not allowed (at 'id'"
+ + " with value '').",
+ "android:id=^\"\"",
+ "android:id=\"\"^");
+ }
+
+ private void checkRanges(String name, String destPath, String aaptError,
+ String expectCaretBegin, String expectCaretEnd)
+ throws Exception {
+ IProject project = getProject();
+ IFile file = getTestDataFile(project, name, destPath);
+
+ // Make file paths absolute
+ String osRoot = project.getLocation().toOSString();
+ String fileRelativePath = file.getProjectRelativePath().toPortableString();
+ String filePath = osRoot + File.separator + fileRelativePath;
+ String originalError = filePath + aaptError.substring(aaptError.indexOf(':'));
+ List<String> errors = Collections.singletonList(originalError);
+
+ // Remove anything already placed there by the project create/build automatic
+ // (this usually only happens while debugging so the background thread has a chance
+ // to get things going)
+ IMarker[] markers = file.findMarkers(AdtConstants.MARKER_AAPT_COMPILE, true,
+ IResource.DEPTH_ZERO);
+ for (IMarker marker : markers) {
+ marker.delete();
+ }
+
+ AaptParser.parseOutput(errors, project);
+ markers = file.findMarkers(AdtConstants.MARKER_AAPT_COMPILE, true,
+ IResource.DEPTH_ZERO);
+ assertNotNull(markers);
+ assertEquals(1, markers.length);
+
+ String fileContents = AdtPlugin.readFile(file);
+ int rangeBegin = getCaretOffset(file, expectCaretBegin);
+ int rangeEnd = getCaretOffset(file, expectCaretEnd);
+
+ // Check text range
+ IMarker marker = markers[0];
+ String message = marker.getAttribute(IMarker.MESSAGE, ""); //$NON-NLS-1$
+ String simplerMessage = aaptError.substring(aaptError.indexOf(' ') + 1);
+ assertEquals(simplerMessage, message);
+ int start = marker.getAttribute(IMarker.CHAR_START, 0);
+ int end = marker.getAttribute(IMarker.CHAR_END, 0);
+
+ assertEquals("Wrong start offset, expected " + expectCaretBegin + " but was "
+ + getCaretContext(fileContents, start), rangeBegin, start);
+ assertEquals("Wrong end offset, expected " + expectCaretEnd + " but was "
+ + getCaretContext(fileContents, end), rangeEnd, end);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFixTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFixTest.java
new file mode 100644
index 0000000..78f16a2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFixTest.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2011 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.build;
+
+import static com.android.AndroidConstants.FD_RES_COLOR;
+import static com.android.AndroidConstants.FD_RES_LAYOUT;
+import static com.android.sdklib.SdkConstants.FD_RES;
+
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
+import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.part.FileEditorInput;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class AaptQuickFixTest extends AdtProjectTest {
+ @Override
+ protected boolean testCaseNeedsUniqueProject() {
+ // Make a separate test project for this test such that we don't pollute code assist
+ // tests with our new resources
+ return true;
+ }
+
+ public void testQuickFix1() throws Exception {
+ // Test adding a value into an existing file (res/values/strings.xml)
+ checkResourceFix("quickfix1.xml", "android:text=\"@string/firs^tstring\"",
+ "res/values/strings.xml");
+ }
+
+ public void testQuickFix2() throws Exception {
+ // Test adding a value into a new file (res/values/dimens.xml, will be created)
+ checkResourceFix("quickfix1.xml", "android:layout_width=\"@dimen/^testdimen\"",
+ "res/values/dimens.xml");
+ }
+
+ public void testQuickFix3() throws Exception {
+ // Test adding a file based resource (uses new file wizard machinery)
+ checkResourceFix("quickfix1.xml", "layout=\"@layout/^testlayout\"",
+ "res/layout/testlayout.xml");
+ }
+
+ public void testQuickFix4() throws Exception {
+ // Test adding a value into a new file (res/values/dimens.xml, will be created)
+ checkNamespaceFix("quickfix2.xml", "<c^olor");
+ }
+
+ private void checkResourceFix(String name, String caretLocation, String expectedNewPath)
+ throws Exception {
+ IProject project = getProject();
+ IFile file = getTestDataFile(project, name, FD_RES + "/" + FD_RES_LAYOUT + "/" + name);
+
+ // Determine the offset
+ final int offset = getCaretOffset(file, caretLocation);
+
+
+ String osRoot = project.getLocation().toOSString();
+ List<String> errors = new ArrayList<String>();
+ String fileRelativePath = file.getProjectRelativePath().toPortableString();
+ String filePath = osRoot + File.separator + fileRelativePath;
+ // Run AaptParser such that markers are added...
+ // When debugging these tests, the project gets a chance to build itself so
+ // the real aapt errors are there. But when the test is run directly, aapt has
+ // not yet run. I tried waiting for the build (using the code in SampleProjectTest)
+ // but this had various adverse effects (exception popups from the Eclipse debugger
+ // etc) so instead this test just hardcodes the aapt errors that should be
+ // observed on quickfix1.xml.
+ assertEquals("Unit test is hardcoded to errors for quickfix1.xml", "quickfix1.xml", name);
+ errors.add(filePath + ":7: error: Error: No resource found that matches the given name"
+ + " (at 'text' with value '@string/firststring').");
+ errors.add(filePath + ":7: error: Error: No resource found that matches the given name"
+ + " (at 'layout_width' with value '@dimen/testdimen').");
+ errors.add(filePath + ":13: error: Error: No resource found that matches the given name"
+ + " (at 'layout' with value '@layout/testlayout').");
+ AaptParser.parseOutput(errors, project);
+
+ AaptQuickFix aaptQuickFix = new AaptQuickFix();
+
+ // Open file
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ assertNotNull(page);
+ IEditorPart editor = IDE.openEditor(page, file);
+ assertTrue(editor instanceof AndroidXmlEditor);
+ AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor;
+ final ISourceViewer viewer = layoutEditor.getStructuredSourceViewer();
+
+ // Test marker resolution.
+ IMarker[] markers = file.findMarkers(AdtConstants.MARKER_AAPT_COMPILE, true,
+ IResource.DEPTH_ZERO);
+ for (IMarker marker : markers) {
+ int start = marker.getAttribute(IMarker.CHAR_START, 0);
+ int end = marker.getAttribute(IMarker.CHAR_END, 0);
+ if (offset >= start && offset <= end) {
+ // Found the target marker. Now check the marker resolution of it.
+ assertTrue(aaptQuickFix.hasResolutions(marker));
+ IMarkerResolution[] resolutions = aaptQuickFix.getResolutions(marker);
+ assertNotNull(resolutions);
+ assertEquals(1, resolutions.length);
+ IMarkerResolution resolution = resolutions[0];
+ assertNotNull(resolution);
+ assertTrue(resolution.getLabel().contains("Create resource"));
+
+ // Not running marker yet -- if we create the files here they already
+ // exist when the quick assist code runs. (The quick fix and the quick assist
+ // mostly share code for the implementation anyway.)
+ //resolution.run(marker);
+ break;
+ }
+ }
+
+ // Next test quick assist.
+
+ IQuickAssistInvocationContext invocationContext = new IQuickAssistInvocationContext() {
+ public int getLength() {
+ return 0;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public ISourceViewer getSourceViewer() {
+ return viewer;
+ }
+ };
+ ICompletionProposal[] proposals = aaptQuickFix
+ .computeQuickAssistProposals(invocationContext);
+ assertNotNull(proposals);
+ assertTrue(proposals.length == 1);
+ ICompletionProposal proposal = proposals[0];
+
+ assertNotNull(proposal.getAdditionalProposalInfo());
+ assertNotNull(proposal.getImage());
+ assertTrue(proposal.getDisplayString().contains("Create resource"));
+
+ IDocument document = new Document();
+ String fileContent = AdtPlugin.readFile(file);
+ document.set(fileContent);
+
+ // Apply quick fix
+ proposal.apply(document);
+
+ IPath path = new Path(expectedNewPath);
+ IFile newFile = project.getFile(path);
+ assertNotNull(path.toPortableString(), newFile);
+
+ // Ensure that the newly created file was opened
+ IEditorPart currentFile = Hyperlinks.getEditor();
+ assertEquals(newFile.getProjectRelativePath(),
+ ((FileEditorInput) currentFile.getEditorInput()).getFile().getProjectRelativePath());
+
+ // Look up caret offset
+ assertTrue(currentFile != null ? currentFile.getClass().getName() : "null",
+ currentFile instanceof AndroidXmlEditor);
+ AndroidXmlEditor newEditor = (AndroidXmlEditor) currentFile;
+ ISourceViewer newViewer = newEditor.getStructuredSourceViewer();
+ Point selectedRange = newViewer.getSelectedRange();
+
+ String newFileContents = AdtPlugin.readFile(newFile);
+
+ // Insert selection markers -- [ ] for the selection range, ^ for the caret
+ String newFileWithCaret = addSelection(newFileContents, selectedRange);
+ newFileWithCaret = removeSessionData(newFileWithCaret);
+
+ assertEqualsGolden(name, newFileWithCaret);
+ }
+
+ private void checkNamespaceFix(String name, String caretLocation)
+ throws Exception {
+ IProject project = getProject();
+ IFile file = getTestDataFile(project, name, FD_RES + "/" + FD_RES_COLOR + "/" + name);
+
+ // Determine the offset
+ final int offset = getCaretOffset(file, caretLocation);
+
+ String osRoot = project.getLocation().toOSString();
+ List<String> errors = new ArrayList<String>();
+ String fileRelativePath = file.getProjectRelativePath().toPortableString();
+ String filePath = osRoot + File.separator + fileRelativePath;
+ assertEquals("Unit test is hardcoded to errors for quickfix2.xml", "quickfix2.xml", name);
+ errors.add(filePath + ":5: error: Error parsing XML: unbound prefix");
+ AaptParser.parseOutput(errors, project);
+
+ AaptQuickFix aaptQuickFix = new AaptQuickFix();
+
+ // Open file
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ assertNotNull(page);
+ IEditorPart editor = IDE.openEditor(page, file);
+ assertTrue(editor instanceof AndroidXmlEditor);
+ AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor;
+ final ISourceViewer viewer = layoutEditor.getStructuredSourceViewer();
+
+ // Test marker resolution.
+ IMarker[] markers = file.findMarkers(AdtConstants.MARKER_AAPT_COMPILE, true,
+ IResource.DEPTH_ZERO);
+ assertEquals(1, markers.length);
+ IMarker marker = markers[0];
+ // Found the target marker. Now check the marker resolution of it.
+ assertTrue(aaptQuickFix.hasResolutions(marker));
+ IMarkerResolution[] resolutions = aaptQuickFix.getResolutions(marker);
+ assertNotNull(resolutions);
+ assertEquals(1, resolutions.length);
+ IMarkerResolution resolution = resolutions[0];
+ assertNotNull(resolution);
+ assertTrue(resolution.getLabel().contains("Insert namespace"));
+
+ // Next test quick assist.
+
+ IQuickAssistInvocationContext invocationContext = new IQuickAssistInvocationContext() {
+ public int getLength() {
+ return 0;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public ISourceViewer getSourceViewer() {
+ return viewer;
+ }
+ };
+ ICompletionProposal[] proposals = aaptQuickFix
+ .computeQuickAssistProposals(invocationContext);
+ assertNotNull(proposals);
+ assertTrue(proposals.length == 1);
+ ICompletionProposal proposal = proposals[0];
+
+ assertNotNull(proposal.getAdditionalProposalInfo());
+ assertNotNull(proposal.getImage());
+ assertTrue(proposal.getDisplayString().contains("Insert namespace"));
+
+ // Open the file to ensure we can get an XML model with getExistingModelForEdit:
+ AdtPlugin.openFile(file, null);
+ IEditorPart newEditor = Hyperlinks.getEditor();
+ assertTrue(newEditor instanceof AndroidXmlEditor);
+
+ AndroidXmlEditor xmlEditor = (AndroidXmlEditor) newEditor;
+ IDocument document = xmlEditor.getStructuredSourceViewer().getDocument();
+
+ // Apply quick fix
+ String before = document.get();
+ proposal.apply(document);
+ String after = document.get();
+ String diff = getDiff(before, after);
+ assertEqualsGolden(name, diff);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssistTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssistTest.java
new file mode 100644
index 0000000..c7a452e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssistTest.java
@@ -0,0 +1,836 @@
+/*
+
+ * Copyright (C) 2011 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.editors;
+
+import static com.android.AndroidConstants.FD_RES_ANIM;
+import static com.android.AndroidConstants.FD_RES_ANIMATOR;
+import static com.android.AndroidConstants.FD_RES_COLOR;
+import static com.android.AndroidConstants.FD_RES_DRAWABLE;
+import static com.android.sdklib.SdkConstants.FD_RES;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.animator.AnimationContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.color.ColorContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.resources.ResourcesContentAssist;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+
+public class AndroidContentAssistTest extends AdtProjectTest {
+ private static final String CARET = "^"; //$NON-NLS-1$
+
+ public void testStartsWith() {
+ assertTrue(AndroidContentAssist.startsWith("", ""));
+ assertTrue(AndroidContentAssist.startsWith("a", ""));
+ assertTrue(AndroidContentAssist.startsWith("A", ""));
+ assertTrue(AndroidContentAssist.startsWith("A", "a"));
+ assertTrue(AndroidContentAssist.startsWith("A", "A"));
+ assertTrue(AndroidContentAssist.startsWith("Ab", "a"));
+ assertTrue(AndroidContentAssist.startsWith("ab", "A"));
+ assertTrue(AndroidContentAssist.startsWith("ab", "AB"));
+ assertFalse(AndroidContentAssist.startsWith("ab", "ABc"));
+ assertFalse(AndroidContentAssist.startsWith("", "ABc"));
+ }
+
+ public void testNameStartsWith() {
+ String fullWord = "android:marginTop";
+ for (int i = 0; i < fullWord.length(); i++) {
+ assertTrue(i + ":" + fullWord.substring(0, i),
+ AndroidContentAssist.nameStartsWith(
+ "android:layout_marginTop", fullWord.substring(0, i), "android:"));
+ }
+
+ fullWord = "android:layout_marginTop";
+ for (int i = 0; i < fullWord.length(); i++) {
+ assertTrue(i + ":" + fullWord.substring(0, i),
+ AndroidContentAssist.nameStartsWith("android:layout_marginTop", fullWord
+ .substring(0, i), "android:"));
+ }
+
+ fullWord = "layout_marginTop";
+ for (int i = 0; i < fullWord.length(); i++) {
+ assertTrue(i + ":" + fullWord.substring(0, i),
+ AndroidContentAssist.nameStartsWith("android:layout_marginTop", fullWord
+ .substring(0, i), "android:"));
+ }
+
+ fullWord = "marginTop";
+ for (int i = 0; i < fullWord.length(); i++) {
+ assertTrue(i + ":" + fullWord.substring(0, i),
+ AndroidContentAssist.nameStartsWith("android:layout_marginTop", fullWord
+ .substring(0, i), "android:"));
+ }
+
+ assertFalse(AndroidContentAssist.nameStartsWith("ab", "ABc", null));
+ assertFalse(AndroidContentAssist.nameStartsWith("", "ABc", null));
+ }
+
+ public void testCompletion1() throws Exception {
+ // Change attribute name completion
+ checkLayoutCompletion("completion1.xml", "layout_w^idth=\"fill_parent\"");
+ }
+
+ public void testCompletion2() throws Exception {
+ // Check attribute value completion for enum
+ checkLayoutCompletion("completion1.xml", "layout_width=\"^fill_parent\"");
+ }
+
+ public void testCompletion3() throws Exception {
+ // Check attribute value completion for enum with a prefix
+ checkLayoutCompletion("completion1.xml", "layout_width=\"fi^ll_parent\"");
+ }
+
+ public void testCompletion4() throws Exception {
+ // Check attribute value completion on units
+ checkLayoutCompletion("completion1.xml", "marginBottom=\"50^\"");
+ }
+
+ public void testCompletion5() throws Exception {
+ // Check attribute value completion on units with prefix
+ checkLayoutCompletion("completion1.xml", "layout_marginLeft=\"50d^p\"");
+ }
+
+ public void testCompletion6() throws Exception {
+ // Check resource sorting - "style" should bubble to the top for a style attribute
+ checkLayoutCompletion("completion1.xml", "style=\"@android:^style/Widget.Button\"");
+ }
+
+ public void testCompletion7a() throws Exception {
+ // Check flags (multiple values inside a single XML value, separated by | - where
+ // the prefix is reset as soon as you pass each | )
+ checkLayoutCompletion("completion1.xml", "android:gravity=\"l^eft|bottom\"");
+ }
+
+ public void testCompletion7b() throws Exception {
+ checkLayoutCompletion("completion1.xml", "android:gravity=\"left|b^ottom\"");
+ }
+
+ public void testCompletion8() throws Exception {
+ // Test completion right at the "=" sign; this will be taken to be the last
+ // character of the attribute name (the caret is between the last char and before
+ // the = characters), so it should match a single attribute
+ checkLayoutCompletion("completion1.xml", "layout_width^=\"fill_parent\"");
+ }
+
+ public void testCompletion9() throws Exception {
+ // Test completion right after the "=" sign; this will be taken to be the beginning
+ // of the attribute value, but all values will also include a leading quote
+ checkLayoutCompletion("completion1.xml", "layout_width=^\"fill_parent\"");
+ }
+
+ public void testCompletion10() throws Exception {
+ // Test completion of element names
+ checkLayoutCompletion("completion1.xml", "<T^extView");
+ }
+
+ public void testCompletion11() throws Exception {
+ // Test completion of element names at the outside of the <. This should include
+ // all the elements too (along with the leading <).
+ checkLayoutCompletion("completion1.xml", "^<TextView");
+ }
+
+ public void testCompletion12() throws Exception {
+ // Test completion of element names inside a nested XML; ensure that this will
+ // correctly compute element names, not previous attribute
+ checkLayoutCompletion("completion1.xml", "btn_default\">^</FrameLayout>");
+ }
+
+ public void testCompletion13a() throws Exception {
+ checkLayoutCompletion("completion2.xml", "gravity=\"left|bottom|^cen");
+ }
+
+ public void testCompletion13b() throws Exception {
+ checkLayoutCompletion("completion2.xml", "gravity=\"left|bottom|cen^");
+ }
+
+ public void testCompletion13c() throws Exception {
+ checkLayoutCompletion("completion2.xml", "gravity=\"left|bottom^|cen");
+ }
+
+ public void testCompletion14() throws Exception {
+ // Test completion of permissions
+ checkManifestCompletion("manifest.xml", "android.permission.ACC^ESS_NETWORK_STATE");
+ }
+
+ public void testCompletion15() throws Exception {
+ // Test completion of intents
+ checkManifestCompletion("manifest.xml", "android.intent.category.L^AUNCHER");
+ }
+
+ public void testCompletion16() throws Exception {
+ // Test completion of top level elements
+ checkManifestCompletion("manifest.xml", "<^application android:i");
+ }
+
+ public void testCompletion17() throws Exception {
+ // Test completion of attributes on the manifest element
+ checkManifestCompletion("manifest.xml", "^android:versionCode=\"1\"");
+ }
+
+ public void testCompletion18() throws Exception {
+ // Test completion of attributes on the manifest element
+ checkManifestCompletion("manifest.xml",
+ "<activity android:^name=\".TestActivity\"");
+ }
+
+ public void testCompletion19() throws Exception {
+ // Test special case where completing on a new element in an otherwise blank line
+ // does not add in full completion (with closing tags)
+ checkLayoutCompletion("broken3.xml", "<EditT^");
+ }
+
+ public void testCompletion20() throws Exception {
+ checkLayoutCompletion("broken1.xml", "android:textColorHigh^");
+ }
+
+ public void testCompletion21() throws Exception {
+ checkLayoutCompletion("broken2.xml", "style=^");
+ }
+
+ public void testCompletion22() throws Exception {
+ // Test completion where the cursor is inside an element (e.g. the next
+ // char is NOT a <) - should not complete with end tags
+ checkLayoutCompletion("completion4.xml", "<Button^");
+ }
+
+ // Test completion in style files
+
+ public void testCompletion23() throws Exception {
+ checkResourceCompletion("completionvalues1.xml", "android:textS^ize");
+ }
+
+ public void testCompletion24() throws Exception {
+ checkResourceCompletion("completionvalues1.xml", "17^sp");
+ }
+
+ public void testCompletion25() throws Exception {
+ checkResourceCompletion("completionvalues1.xml", "textColor\">^@color/title_color</item>");
+ }
+
+ public void testCompletion26() throws Exception {
+ checkResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:shadowColor\">@an^</item>");
+ }
+
+ public void testCompletion27() throws Exception {
+ checkResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:gravity\">^ </item>");
+ }
+
+ public void testCompletion28() throws Exception {
+ checkResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:gravity\"> ^</item>");
+ }
+
+ public void testCompletion29() throws Exception {
+ checkResourceCompletion("completionvalues1.xml", "<item name=\"gr^\">");
+ }
+
+ public void testCompletion30() throws Exception {
+ checkResourceCompletion("completionvalues1.xml", "<item name=\"an^\">");
+ }
+
+ public void testCompletion31() throws Exception {
+ checkResourceCompletion("completionvalues1.xml", "<item ^></item>");
+ }
+
+ public void testCompletion32() throws Exception {
+ checkResourceCompletion("completionvalues1.xml", "<item name=\"^\"></item>");
+ }
+
+ public void testCompletion33() throws Exception {
+ checkResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:allowSingleTap\">^</item>");
+ }
+
+ public void testCompletion34() throws Exception {
+ checkResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:alwaysDrawnWithCache\">^ false </item>");
+ }
+
+ public void testCompletion35() throws Exception {
+ checkResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:alwaysDrawnWithCache\"> ^false </item>");
+ }
+
+ public void testCompletion36() throws Exception {
+ checkResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:alwaysDrawnWithCache\"> f^alse </item>");
+ }
+
+ public void testCompletion37() throws Exception {
+ checkResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:orientation\">h^</item>");
+ }
+
+ public void testCompletion38() throws Exception {
+ checkResourceCompletion("completionvalues1.xml",
+ " c^");
+ }
+
+ public void testCompletion39() throws Exception {
+ // If you are at the end of a closing quote (but with no space), completion should
+ // include a separating space.
+ checkLayoutCompletion("completion1.xml", "marginBottom=\"50\"^");
+ }
+
+ public void testCompletion40() throws Exception {
+ // Same as test 39 but with single quote
+ checkLayoutCompletion("completion5.xml", "android:id='@+id/button2'^");
+ }
+
+ public void testCompletion41() throws Exception {
+ // Test prefix matching on layout_ with namespace prefix
+ checkLayoutCompletion("completion8.xml", "android:mar^=\"50dp\"");
+ }
+
+ public void testCompletion42() throws Exception {
+ // Test prefix matching on layout_ with namespace prefix
+ checkLayoutCompletion("completion8.xml", "android:w^i=\"100\"");
+ }
+
+ public void testCompletion43() throws Exception {
+ // Test prefix matching on layout_ without namespace prefix
+ checkLayoutCompletion("completion8.xml", "mar^=\"60dp\"");
+ }
+
+ public void testCompletion44() throws Exception {
+ // Test prefix matching on layout_ without namespace prefix
+ checkLayoutCompletion("completion8.xml", "android:layo^ut_width=\"fill_parent\"");
+ }
+
+ public void testCompletion45() throws Exception {
+ // Test top level elements in colors
+ checkColorCompletion("color1.xml", "^<selector");
+ }
+
+ public void testCompletion46a() throws Exception {
+ // Test children of selector: should offer item
+ checkColorCompletion("color1.xml", "^<item android");
+ }
+
+ public void testCompletion46b() throws Exception {
+ // Test attribute matching in color files
+ checkColorCompletion("color1.xml", "<item ^android:state_focused=\"true\"/>");
+ }
+
+ public void testCompletion47() throws Exception {
+ // Check root completion in drawables: should list all drawable root elements
+ checkDrawableCompletion("drawable1.xml", "^<layer-list");
+ }
+
+ public void testCompletion48() throws Exception {
+ // Check attributes of the layer list
+ checkDrawableCompletion("drawable1.xml", "^xmlns:android");
+ }
+
+ public void testCompletion49() throws Exception {
+ // Check attributes of the <item> element inside a <layer-list>
+ checkDrawableCompletion("drawable1.xml", "<item ^></item>");
+ }
+
+ public void testCompletion50() throws Exception {
+ // Check elements nested inside the <item> in a layer list: can use any drawable again
+ checkDrawableCompletion("drawable1.xml", "<item >^</item>");
+ }
+
+ public void testCompletion51() throws Exception {
+ // Check attributes of <shape> element
+ checkDrawableCompletion("drawable2.xml", "^android:innerRadiusRatio=\"2\"");
+ }
+
+ public void testCompletion52() throws Exception {
+ // Check list of available elements inside a shape
+ checkDrawableCompletion("drawable2.xml", "^<gradient");
+ }
+
+ public void testCompletion53() throws Exception {
+ // Check list of root anim elements
+ checkAnimCompletion("anim1.xml", "^<set xmlns");
+ }
+
+ public void testCompletion54() throws Exception {
+ // Check that we can nest inside <set>'s
+ checkAnimCompletion("anim1.xml", "^<translate android:id=");
+ }
+
+ public void testCompletion55() throws Exception {
+ // translate properties
+ checkAnimCompletion("anim1.xml", "android:^fromXDelta=");
+ }
+
+ public void testCompletion56() throws Exception {
+ // alpha properties
+ checkAnimCompletion("anim1.xml", "android:^fromAlpha=");
+ }
+
+ public void testCompletion57() throws Exception {
+ // Fractional properties
+ checkAnimCompletion("anim1.xml", "android:fromXDelta=\"100^%p\"");
+ }
+
+ public void testCompletion58() throws Exception {
+ // Top level animator elements
+ checkAnimatorCompletion("animator1.xml", "^<set xmlns");
+ }
+
+ public void testCompletion59() throws Exception {
+ // objectAnimator properties
+ checkAnimatorCompletion("animator1.xml", "android:^duration=\"2000\"");
+ }
+
+ public void testCompletion60() throws Exception {
+ // propertyName completion
+ checkAnimatorCompletion("animator1.xml", "android:propertyName=\"scal^eX\"/>");
+ }
+
+ public void testCompletion61() throws Exception {
+ // Interpolator completion
+ checkAnimatorCompletion("animator1.xml",
+ "android:interpolator=\"^@android:anim/bounce_interpolator\"");
+ }
+
+ // ---- Test *applying* code completion ----
+
+
+
+ // The following tests check -applying- a specific code completion
+ // match - this verifies that the document is updated correctly, the
+ // caret is moved appropriately, etc.
+
+ public void testApplyCompletion1() throws Exception {
+ // Change attribute name completion
+ checkApplyLayoutCompletion("completion1.xml", "layout_w^idth=\"fill_parent\"",
+ "android:layout_weight");
+ }
+
+ public void testApplyCompletion2() throws Exception {
+ // Check attribute value completion for enum
+ checkApplyLayoutCompletion("completion1.xml", "layout_width=\"^fill_parent\"",
+ "match_parent");
+ }
+
+ public void testApplyCompletion3() throws Exception {
+ // Check attribute value completion for enum with a prefix
+ checkApplyLayoutCompletion("completion1.xml", "layout_width=\"fi^ll_parent\"",
+ "fill_parent");
+ }
+
+ public void testApplyCompletion4() throws Exception {
+ // Check attribute value completion on units
+ checkApplyLayoutCompletion("completion1.xml", "marginBottom=\"50^\"", "50mm");
+ }
+
+ public void testApplyCompletion5() throws Exception {
+ // Check attribute value completion on units with prefix
+ checkApplyLayoutCompletion("completion1.xml", "layout_marginLeft=\"50d^p\"", "50dp");
+ }
+
+ public void testApplyCompletion6() throws Exception {
+ // Check resource sorting - "style" should bubble to the top for a style attribute
+ checkApplyLayoutCompletion("completion1.xml", "style=\"@android:^style/Widget.Button\"",
+ "@android:drawable/");
+ }
+
+ public void testApplyCompletion7a() throws Exception {
+ // Check flags (multiple values inside a single XML value, separated by | - where
+ // the prefix is reset as soon as you pass each | )
+ checkApplyLayoutCompletion("completion1.xml", "android:gravity=\"l^eft|bottom\"",
+ "left");
+ // NOTE - this will replace all flag values with the newly selected value.
+ // That may not be the best behavior - perhaps we should only replace one portion
+ // of the value.
+ }
+
+ public void testApplyCompletion7b() throws Exception {
+ checkApplyLayoutCompletion("completion1.xml", "android:gravity=\"left|b^ottom\"",
+ "bottom");
+ // NOTE - this will replace all flag values with the newly selected value.
+ // That may not be the best behavior - perhaps we should only replace one portion
+ // of the value.
+ }
+
+ public void testApplyCompletion8() throws Exception {
+ // Test completion right at the "=" sign; this will be taken to be the last
+ // character of the attribute name (the caret is between the last char and before
+ // the = characters), so it should match a single attribute
+ checkApplyLayoutCompletion("completion1.xml", "layout_width^=\"fill_parent\"",
+ "android:layout_width");
+ }
+
+ public void testApplyCompletion9() throws Exception {
+ // Test completion right after the "=" sign; this will be taken to be the beginning
+ // of the attribute value, but all values will also include a leading quote
+ checkApplyLayoutCompletion("completion1.xml", "layout_width=^\"fill_parent\"",
+ "\"wrap_content\"");
+ }
+
+ public void testApplyCompletion10() throws Exception {
+ // Test completion of element names
+ checkApplyLayoutCompletion("completion1.xml", "<T^extView", "TableLayout");
+ }
+
+ public void testApplyCompletion11a() throws Exception {
+ // Test completion of element names at the outside of the <. This should include
+ // all the elements too (along with the leading <).
+ checkApplyLayoutCompletion("completion1.xml", "^<TextView", "<RadioGroup ></RadioGroup>");
+ }
+
+ public void testApplyCompletion11b() throws Exception {
+ // Similar to testApplyCompletion11a, but replacing with an element that does not
+ // have children (to test the closing tag insertion code)
+ checkApplyLayoutCompletion("completion1.xml", "^<TextView", "<CheckBox />");
+ }
+
+ public void testApplyCompletion12() throws Exception {
+ // Test completion of element names inside a nested XML; ensure that this will
+ // correctly compute element names, not previous attribute
+ checkApplyLayoutCompletion("completion1.xml", "btn_default\">^</FrameLayout>",
+ "<FrameLayout ></FrameLayout>");
+ }
+
+ public void testApplyCompletion13a() throws Exception {
+ checkApplyLayoutCompletion("completion2.xml", "gravity=\"left|bottom|^cen",
+ "fill_vertical");
+ }
+
+ public void testApplyCompletion13b() throws Exception {
+ checkApplyLayoutCompletion("completion2.xml", "gravity=\"left|bottom|cen^",
+ "center_horizontal");
+ }
+
+ public void testApplyCompletion13c() throws Exception {
+ checkApplyLayoutCompletion("completion2.xml", "gravity=\"left|bottom^|cen",
+ "bottom|fill_horizontal");
+ }
+
+ public void testApplyCompletion14() throws Exception {
+ // Test special case where completing on a new element in an otherwise blank line
+ // does not add in full completion (with closing tags)
+ checkApplyLayoutCompletion("broken3.xml", "<EditT^", "EditText />");
+ }
+
+ public void testApplyCompletion15() throws Exception {
+ checkApplyLayoutCompletion("broken1.xml", "android:textColorHigh^",
+ "android:textColorHighlight");
+ }
+
+ public void testApplyCompletion16() throws Exception {
+ checkApplyLayoutCompletion("broken2.xml", "style=^",
+ "\"@android:\"");
+ }
+
+ public void testApplyCompletion17() throws Exception {
+ // Make sure that completion right before a / inside an element still
+ // inserts the ="" part (e.g. handles it as "insertNew)
+ checkApplyLayoutCompletion("completion3.xml", "<EditText ^/>",
+ "android:textColorHighlight");
+ }
+
+ public void testApplyCompletion18() throws Exception {
+ // Make sure that completion right before a > inside an element still
+ // inserts the ="" part (e.g. handles it as "insertNew)
+ checkApplyLayoutCompletion("completion3.xml", "<Button ^></Button>",
+ "android:paddingRight");
+ }
+
+ public void testApplyCompletion19() throws Exception {
+ // Test completion with single quotes (apostrophe)
+ checkApplyLayoutCompletion("completion5.xml", "android:orientation='^'", "horizontal");
+ }
+
+ public void testApplyCompletion20() throws Exception {
+ // Test completion with single quotes (apostrophe)
+ checkApplyLayoutCompletion("completion5.xml", "android:layout_marginTop='50^dp'", "50pt");
+ }
+
+ public void testApplyCompletion21() throws Exception {
+ // Test completion with single quotes (apostrophe)
+ checkApplyLayoutCompletion("completion5.xml", "android:layout_width='^wrap_content'",
+ "match_parent");
+ // Still broken - but not a common case
+ //checkApplyLayoutCompletion("completion5.xml", "android:layout_width=^'wrap_content'",
+ // "\"match_parent\"");
+ }
+
+ public void testApplyCompletion22() throws Exception {
+ // Test completion in an empty string
+ checkApplyLayoutCompletion("completion6.xml", "android:orientation=\"^\"", "horizontal");
+ }
+
+ public void testApplyCompletion23() throws Exception {
+ // Test completion in an empty string
+ checkApplyLayoutCompletion("completion7.xml", "android:orientation=\"^", "horizontal");
+ }
+
+ // Test completion in style files
+
+ public void testApplyCompletion24a() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml", "android:textS^ize",
+ "android:textSelectHandleLeft");
+ }
+
+ public void testApplyCompletion24b() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml", "17^sp", "17mm");
+ }
+
+ public void testApplyCompletion25() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ "textColor\">^@color/title_color</item>", "@android:");
+ }
+
+ public void testApplyCompletion26() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:shadowColor\">@an^</item>", "@android:");
+ }
+
+ public void testApplyCompletion27() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:gravity\">^ </item>", "center_vertical");
+ }
+
+ public void testApplyCompletion28() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:gravity\"> ^</item>", "left");
+ }
+
+ public void testApplyCompletion29() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml", "<item name=\"gr^\">",
+ "android:gravity");
+ }
+
+ public void testApplyCompletion30() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml", "<item name=\"an^\">",
+ "android:animateOnClick");
+ }
+
+ public void testApplyCompletion31() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml", "<item ^></item>", "name");
+ }
+
+ public void testApplyCompletion32() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml", "<item name=\"^\"></item>",
+ "android:background");
+ }
+
+ public void testApplyCompletion33() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:allowSingleTap\">^</item>", "true");
+ }
+
+ public void testApplyCompletion34() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:alwaysDrawnWithCache\">^ false </item>", "true");
+ }
+
+ public void testApplyCompletion35() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:alwaysDrawnWithCache\"> ^false </item>", "true");
+ }
+
+ public void testApplyCompletion36() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:alwaysDrawnWithCache\"> f^alse </item>", "false");
+ }
+
+ public void testApplyCompletion37() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ "<item name=\"android:orientation\">h^</item>", "horizontal");
+ }
+
+ public void testApplyCompletion38() throws Exception {
+ checkApplyResourceCompletion("completionvalues1.xml",
+ " c^", "center");
+ }
+
+ public void testApplyCompletion39() throws Exception {
+ // If you are at the end of a closing quote (but with no space), completion should
+ // include a separating space.
+ checkApplyLayoutCompletion("completion1.xml", "marginBottom=\"50\"^", " android:maxEms");
+ }
+
+ public void testApplyCompletion40() throws Exception {
+ // If you are at the end of a closing quote (but with no space), completion should
+ // include a separating space.
+ checkApplyLayoutCompletion("completion5.xml", "android:id='@+id/button2'^",
+ " android:maxWidth");
+ }
+
+ public void testApplyCompletion41() throws Exception {
+ // Test prefix matching on layout_ with namespace prefix
+ checkApplyLayoutCompletion("completion8.xml", "android:mar^=\"50dp\"",
+ "android:layout_marginRight");
+ }
+
+ // --- Code Completion test infrastructure ----
+
+ private void checkLayoutCompletion(String name, String caretLocation) throws Exception {
+ checkCompletion(name, getLayoutFile(getProject(), name), caretLocation,
+ new LayoutContentAssist());
+ }
+
+ private void checkColorCompletion(String name, String caretLocation) throws Exception {
+ IFile file = getTestDataFile(getProject(), name,
+ FD_RES + "/" + FD_RES_COLOR + "/" + name);
+ checkCompletion(name, file, caretLocation,
+ new ColorContentAssist());
+ }
+ private void checkAnimCompletion(String name, String caretLocation) throws Exception {
+ IFile file = getTestDataFile(getProject(), name,
+ FD_RES + "/" + FD_RES_ANIM + "/" + name);
+ checkCompletion(name, file, caretLocation,
+ new AnimationContentAssist());
+ }
+
+ private void checkAnimatorCompletion(String name, String caretLocation) throws Exception {
+ IFile file = getTestDataFile(getProject(), name,
+ FD_RES + "/" + FD_RES_ANIMATOR + "/" + name);
+ checkCompletion(name, file, caretLocation,
+ new AnimationContentAssist());
+ }
+
+
+ private void checkDrawableCompletion(String name, String caretLocation) throws Exception {
+ IFile file = getTestDataFile(getProject(), name,
+ FD_RES + "/" + FD_RES_DRAWABLE + "/" + name);
+ checkCompletion(name, file, caretLocation,
+ new DrawableContentAssist());
+ }
+
+ private void checkManifestCompletion(String name, String caretLocation) throws Exception {
+ // Manifest files must be named AndroidManifest.xml. Must overwrite to replace
+ // the default manifest created in the test project.
+ IFile file = getTestDataFile(getProject(), name, "AndroidManifest.xml", true);
+
+ checkCompletion(name, file, caretLocation,
+ new ManifestContentAssist());
+ }
+
+ private void checkApplyLayoutCompletion(String name, String caretLocation,
+ String match) throws Exception {
+ checkApplyCompletion(name, getLayoutFile(getProject(), name), caretLocation,
+ new LayoutContentAssist(), match);
+ }
+
+ private void checkResourceCompletion(String name, String caretLocation) throws Exception {
+ checkCompletion(name, getValueFile(getProject(), name), caretLocation,
+ new ResourcesContentAssist());
+ }
+
+ private void checkApplyResourceCompletion(String name, String caretLocation,
+ String match) throws Exception {
+ checkApplyCompletion(name, getValueFile(getProject(), name), caretLocation,
+ new ResourcesContentAssist(), match);
+ }
+
+ private ICompletionProposal[] complete(IFile file, String caretLocation,
+ AndroidContentAssist assist) throws Exception {
+
+ // Determine the offset
+ int offset = getCaretOffset(file, caretLocation);
+
+ // Open file
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ assertNotNull(page);
+ IEditorPart editor = IDE.openEditor(page, file);
+ assertTrue(editor instanceof AndroidXmlEditor);
+ AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor;
+ ISourceViewer viewer = layoutEditor.getStructuredSourceViewer();
+
+ // Run code completion
+ ICompletionProposal[] proposals = assist.computeCompletionProposals(viewer, offset);
+ if (proposals == null) {
+ proposals = new ICompletionProposal[0];
+ }
+
+ return proposals;
+ }
+
+ private void checkApplyCompletion(String basename, IFile file, String caretLocation,
+ AndroidContentAssist assist, String match) throws Exception {
+ ICompletionProposal[] proposals = complete(file, caretLocation, assist);
+ ICompletionProposal chosen = null;
+ for (ICompletionProposal proposal : proposals) {
+ if (proposal.getDisplayString().equals(match)) {
+ chosen = proposal;
+ break;
+ }
+ }
+ assertNotNull(chosen);
+ assert chosen != null; // Eclipse null pointer analysis doesn't believe the JUnit assertion
+
+ String fileContent = AdtPlugin.readFile(file);
+ IDocument document = new Document();
+ document.set(fileContent);
+
+ // Apply code completion
+ chosen.apply(document);
+
+ // Insert caret location as well
+ Point location = chosen.getSelection(document);
+ document.replace(location.x, 0, CARET);
+
+ String actual = document.get();
+
+ int offset = getCaretOffset(fileContent, caretLocation);
+ String beforeWithCaret = fileContent.substring(0, offset) + CARET
+ + fileContent.substring(offset);
+
+ String diff = getDiff(beforeWithCaret, actual);
+ assertTrue(diff + " versus " + actual, diff.length() > 0 || beforeWithCaret.equals(actual));
+
+ StringBuilder summary = new StringBuilder();
+ summary.append("Code completion in " + basename + " for " + caretLocation + " selecting " + match + ":\n");
+ if (diff.length() == 0) {
+ diff = "No changes";
+ }
+ summary.append(diff);
+
+ //assertEqualsGolden(basename, actual);
+ assertEqualsGolden(basename, summary.toString(), "diff");
+ }
+
+ private void checkCompletion(String basename, IFile file, String caretLocation,
+ AndroidContentAssist assist) throws Exception {
+ ICompletionProposal[] proposals = complete(file, caretLocation, assist);
+ StringBuilder sb = new StringBuilder(1000);
+ sb.append("Code completion in " + basename + " for " + caretLocation + ":\n");
+ for (ICompletionProposal proposal : proposals) {
+ // TODO: assertNotNull(proposal.getImage());
+ sb.append(proposal.getDisplayString());
+ String help = proposal.getAdditionalProposalInfo();
+ if (help != null && help.trim().length() > 0) {
+ sb.append(" : ");
+ sb.append(help.replace('\n', ' '));
+ }
+ sb.append('\n');
+ }
+ assertEqualsGolden(basename, sb.toString(), "txt");
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadataTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadataTest.java
new file mode 100644
index 0000000..7dc3f2d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadataTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.gle2;
+
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
+
+import com.android.ide.common.layout.BaseLayoutRule;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.w3c.dom.Node;
+
+@SuppressWarnings("restriction") // XML DOM model
+public class LayoutMetadataTest extends AdtProjectTest {
+ public void testMetadata1() throws Exception {
+ Pair<IDocument, UiElementNode> pair = getNode("metadata.xml", "listView1");
+ IDocument document = pair.getFirst();
+ UiElementNode uiNode = pair.getSecond();
+ Node node = uiNode.getXmlNode();
+
+ LayoutMetadata metadata = LayoutMetadata.get();
+ assertNull(metadata.getProperty(document, node, "foo"));
+ String before =
+ "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
+ " android:layout_height=\"wrap_content\">\n" +
+ " </ListView>";
+ assertEquals(before, getText(document, node));
+
+ // Set the property
+ metadata.setProperty(document, node,
+ "listitem", "@android:layout/simple_list_item_checked");
+ String after =
+ "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
+ " android:layout_height=\"wrap_content\">\n" +
+ " <!-- Preview: listitem=@android:layout/simple_list_item_checked -->\n" +
+ " </ListView>";
+ assertEquals(after, getText(document, node));
+
+ // Set a second property
+ metadata.setProperty(document, node,
+ "listheader", "@android:layout/browser_link_context_header");
+ after =
+ "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
+ " android:layout_height=\"wrap_content\">\n" +
+ " <!-- Preview: \n" +
+ " listheader=@android:layout/browser_link_context_header\n" +
+ " listitem=@android:layout/simple_list_item_checked\n" +
+ " -->\n" +
+ " </ListView>";
+ assertEquals(after, getText(document, node));
+
+ // Set list item to a different layout
+ metadata.setProperty(document, node,
+ "listitem", "@android:layout/simple_list_item_single_choice");
+ after =
+ "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
+ " android:layout_height=\"wrap_content\">\n" +
+ " <!-- Preview: \n" +
+ " listheader=@android:layout/browser_link_context_header\n" +
+ " listitem=@android:layout/simple_list_item_single_choice\n" +
+ " -->\n" +
+ " </ListView>";
+ assertEquals(after, getText(document, node));
+
+ // Set header to a different layout
+ metadata.setProperty(document, node,
+ "listheader", "@layout/foo");
+ after =
+ "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
+ " android:layout_height=\"wrap_content\">\n" +
+ " <!-- Preview: \n" +
+ " listheader=@layout/foo\n" +
+ " listitem=@android:layout/simple_list_item_single_choice\n" +
+ " -->\n" +
+ " </ListView>";
+ assertEquals(after, getText(document, node));
+
+ // Clear out list item
+ metadata.setProperty(document, node,
+ "listitem", null);
+ after =
+ "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
+ " android:layout_height=\"wrap_content\">\n" +
+ " <!-- Preview: listheader=@layout/foo -->\n" +
+ " </ListView>";
+ assertEquals(after, getText(document, node));
+
+ // Clear out list header
+ metadata.setProperty(document, node,
+ "listheader", null);
+ after =
+ "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
+ " android:layout_height=\"wrap_content\"></ListView>";
+ assertEquals(after, getText(document, node));
+
+ // Check node expansion on the button which doesn't have an end tag:
+ before = "<Button android:text=\"Button\" android:id=\"@+id/button1\"/>";
+ }
+
+ public void testMetadata2() throws Exception {
+ Pair<IDocument, UiElementNode> pair = getNode("metadata.xml", "button1");
+ IDocument document = pair.getFirst();
+ UiElementNode uiNode = pair.getSecond();
+ Node node = uiNode.getXmlNode();
+
+ LayoutMetadata metadata = LayoutMetadata.get();
+ assertNull(metadata.getProperty(document, node, "foo"));
+ String before =
+ "<Button android:text=\"Button\" android:id=\"@+id/button1\"/>";
+ assertEquals(before, getText(document, node));
+
+ // Set the property
+ metadata.setProperty(document, node,
+ "listitem", "@android:layout/simple_list_item_checked");
+ String after =
+ "<Button android:text=\"Button\" android:id=\"@+id/button1\">\n" +
+ " <!-- Preview: listitem=@android:layout/simple_list_item_checked -->\n" +
+ " </Button>";
+ assertEquals(after, getText(document, node));
+ }
+
+ // ==== Test utilities ====
+
+ private static String getText(IDocument document, Node node) throws Exception {
+ IndexedRegion region = (IndexedRegion) node;
+ // This often returns the wrong value:
+ //int length = region.getLength();
+ int length = region.getEndOffset() - region.getStartOffset();
+ return document.get(region.getStartOffset(), length);
+ }
+
+ private Pair<IDocument, UiElementNode> getNode(String filename, String targetId)
+ throws Exception, PartInitException {
+ IFile file = getLayoutFile(getProject(), filename);
+ AdtPlugin.openFile(file, null);
+ IEditorPart newEditor = Hyperlinks.getEditor();
+ assertTrue(newEditor instanceof AndroidXmlEditor);
+ AndroidXmlEditor xmlEditor = (AndroidXmlEditor) newEditor;
+ IStructuredDocument document = xmlEditor.getStructuredDocument();
+ UiElementNode root = xmlEditor.getUiRootNode();
+ assertNotNull(root);
+ UiElementNode node = findById(root, targetId);
+ assertNotNull(node);
+ Pair<IDocument, UiElementNode> pair = Pair.<IDocument, UiElementNode>of(document, node);
+ return pair;
+ }
+
+ private static UiElementNode findById(UiElementNode node, String targetId) {
+ assertFalse(targetId.startsWith(NEW_ID_PREFIX));
+ assertFalse(targetId.startsWith(ID_PREFIX));
+
+ String id = node.getAttributeValue(ATTR_ID);
+ if (id != null && targetId.equals(BaseLayoutRule.stripIdPrefix(id))) {
+ return node;
+ }
+
+ for (UiElementNode child : node.getUiChildren()) {
+ UiElementNode result = findById(child, targetId);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java
new file mode 100644
index 0000000..3b83bd7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.AndroidConstants.FD_RES_LAYOUT;
+import static com.android.AndroidConstants.FD_RES_VALUES;
+import static com.android.sdklib.SdkConstants.FD_RES;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreationPage;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizard;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.NewTestProjectCreationPage;
+import com.android.ide.eclipse.tests.SdkTestCase;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkingSet;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+@SuppressWarnings("restriction")
+public class AdtProjectTest extends SdkTestCase {
+ private static final int TARGET_API_LEVEL = 11;
+ /** Update golden files if different from the actual results */
+ private static final boolean UPDATE_DIFFERENT_FILES = false;
+ /** Create golden files if missing */
+ private static final boolean UPDATE_MISSING_FILES = true;
+ private static final String TEST_DATA_REL_PATH =
+ "eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/"
+ + "internal/editors/layout/refactoring/testdata";
+ private static final String PROJECTNAME_PREFIX = "testproject-";
+ private static final long TESTS_START_TIME = System.currentTimeMillis();
+ private static File sTempDir = null;
+
+ /**
+ * We don't stash the project used by each test case as a field such that test cases
+ * can share a single project instance (which is typically much faster).
+ * However, see {@link #getProjectName()} for exceptions to this sharing scheme.
+ */
+ private static Map<String, IProject> sProjectMap = new HashMap<String, IProject>();
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // Prevent preview icon computation during plugin test to make test faster
+ if (AdtPlugin.getDefault() == null) {
+ fail("This test must be run as an Eclipse plugin test, not a plain JUnit test!");
+ }
+ AdtPrefs.getPrefs().setPaletteModes("ICON_TEXT"); //$NON-NLS-1$
+
+ getProject();
+ }
+
+ /** Set to true if the subclass test case should use a per-instance project rather
+ * than a shared project. This is needed by projects which modify the project in such
+ * a way that it affects what other tests see (for example, the quickfix resource creation
+ * tests will add in new resources, which the code completion tests will then list as
+ * possible matches if the code completion test is run after the quickfix test.)
+ * @return true to create a per-instance project instead of the default shared project
+ */
+ protected boolean testCaseNeedsUniqueProject() {
+ return false;
+ }
+
+ protected boolean testNeedsUniqueProject() {
+ return false;
+ }
+
+ @Override
+ protected boolean validateSdk(IAndroidTarget target) {
+ // Not quite working yet. When enabled will make tests run faster.
+ //if (target.getVersion().getApiLevel() < TARGET_API_LEVEL) {
+ // return false;
+ //}
+
+ return true;
+ }
+
+ /** Returns a name to use for the project used in this test. Subclasses do not need to
+ * override this if they can share a project with others - which is the case if they do
+ * not modify the project in a way that does not affect other tests. For example
+ * the resource quickfix test will create new resources which affect what shows up
+ * in the code completion results, so the quickfix tests will override this method
+ * to produce a unique project for its own tests.
+ */
+ private String getProjectName() {
+ if (testNeedsUniqueProject()) {
+ return PROJECTNAME_PREFIX + getClass().getSimpleName() + "-" + getName();
+ } else if (testCaseNeedsUniqueProject()) {
+ return PROJECTNAME_PREFIX + getClass().getSimpleName();
+ } else {
+ return PROJECTNAME_PREFIX + TESTS_START_TIME;
+ }
+ }
+
+ protected IProject getProject() {
+ String projectName = getProjectName();
+ IProject project = sProjectMap.get(projectName);
+ if (project == null) {
+ project = createProject(projectName);
+ assertNotNull(project);
+ sProjectMap.put(projectName, project);
+ }
+
+ return project;
+ }
+
+ protected IFile getTestDataFile(IProject project, String name) throws Exception {
+ return getTestDataFile(project, name, name);
+ }
+
+ protected IFile getLayoutFile(IProject project, String name) throws Exception {
+ return getTestDataFile(project, name, FD_RES + "/" + FD_RES_LAYOUT + "/" + name);
+ }
+
+ protected IFile getValueFile(IProject project, String name) throws Exception {
+ return getTestDataFile(project, name, FD_RES + "/" + FD_RES_VALUES + "/" + name);
+ }
+
+ protected IFile getTestDataFile(IProject project, String sourceName,
+ String destPath) throws Exception {
+ return getTestDataFile(project, sourceName, destPath, false);
+ }
+
+ protected IFile getTestDataFile(IProject project, String sourceName,
+ String destPath, boolean overwrite) throws Exception {
+ String[] split = destPath.split("/"); //$NON-NLS-1$
+ IContainer parent;
+ String name;
+ if (split.length == 1) {
+ parent = project;
+ name = destPath;
+ } else {
+ IFolder folder = project.getFolder(split[0]);
+ NullProgressMonitor monitor = new NullProgressMonitor();
+ if (!folder.exists()) {
+ folder.create(true /* force */, true /* local */, monitor);
+ }
+ for (int i = 1, n = split.length; i < n -1; i++) {
+ IFolder subFolder = folder.getFolder(split[i]);
+ if (!subFolder.exists()) {
+ subFolder.create(true /* force */, true /* local */, monitor);
+ }
+ folder = subFolder;
+ }
+ name = split[split.length - 1];
+ parent = folder;
+ }
+ IFile file = parent.getFile(new Path(name));
+ if (overwrite && file.exists()) {
+ String currentContents = AdtPlugin.readFile(file);
+ String newContents = readTestFile(sourceName, true);
+ if (currentContents == null || !currentContents.equals(newContents)) {
+ file.delete(true, new NullProgressMonitor());
+ } else {
+ return file;
+ }
+ }
+ if (!file.exists()) {
+ String xml = readTestFile(sourceName, true);
+ InputStream bstream = new ByteArrayInputStream(xml.getBytes("UTF-8")); //$NON-NLS-1$
+ NullProgressMonitor monitor = new NullProgressMonitor();
+ file.create(bstream, false /* force */, monitor);
+ }
+
+ return file;
+ }
+
+ protected IProject createProject(String name) {
+ IAndroidTarget target = null;
+
+ IAndroidTarget[] targets = getSdk().getTargets();
+ for (IAndroidTarget t : targets) {
+ if (t.getVersion().getApiLevel() >= TARGET_API_LEVEL) {
+ target = t;
+ break;
+ }
+ }
+ assertNotNull(target);
+
+ final StubProjectWizard newProjCreator = new StubProjectWizard(
+ name, target);
+ newProjCreator.init(null, null);
+ // need to run finish on ui thread since it invokes a perspective switch
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ newProjCreator.performFinish();
+ }
+ });
+
+ return validateProjectExists(name);
+ }
+
+ public void createTestProject() {
+ IAndroidTarget target = null;
+
+ IAndroidTarget[] targets = getSdk().getTargets();
+ for (IAndroidTarget t : targets) {
+ if (t.getVersion().getApiLevel() >= TARGET_API_LEVEL) {
+ target = t;
+ break;
+ }
+ }
+ assertNotNull(target);
+ }
+
+ private static IProject validateProjectExists(String name) {
+ IProject iproject = getProject(name);
+ assertTrue(String.format("%s project not created", name), iproject.exists());
+ assertTrue(String.format("%s project not opened", name), iproject.isOpen());
+ return iproject;
+ }
+
+ private static IProject getProject(String name) {
+ IProject iproject = ResourcesPlugin.getWorkspace().getRoot()
+ .getProject(name);
+ return iproject;
+ }
+
+ protected int getCaretOffset(IFile file, String caretLocation) {
+ assertTrue(caretLocation, caretLocation.contains("^"));
+
+ String fileContent = AdtPlugin.readFile(file);
+ return getCaretOffset(fileContent, caretLocation);
+ }
+
+ protected int getCaretOffset(String fileContent, String caretLocation) {
+ assertTrue(caretLocation, caretLocation.contains("^"));
+
+ int caretDelta = caretLocation.indexOf("^");
+ assertTrue(caretLocation, caretDelta != -1);
+ String caretContext = caretLocation.substring(0, caretDelta)
+ + caretLocation.substring(caretDelta + 1); // +1: skip "^"
+ int caretContextIndex = fileContent.indexOf(caretContext);
+ assertTrue("Caret content " + caretContext + " not found in file",
+ caretContextIndex != -1);
+ return caretContextIndex + caretDelta;
+ }
+
+ protected String addSelection(String newFileContents, Point selectedRange) {
+ int selectionBegin = selectedRange.x;
+ int selectionEnd = selectionBegin + selectedRange.y;
+ return addSelection(newFileContents, selectionBegin, selectionEnd);
+ }
+
+ protected String addSelection(String newFileContents, int selectionBegin, int selectionEnd) {
+ // Insert selection markers -- [ ] for the selection range, ^ for the caret
+ String newFileWithCaret;
+ if (selectionBegin < selectionEnd) {
+ newFileWithCaret = newFileContents.substring(0, selectionBegin) + "[^"
+ + newFileContents.substring(selectionBegin, selectionEnd) + "]"
+ + newFileContents.substring(selectionEnd);
+ } else {
+ // Selected range
+ newFileWithCaret = newFileContents.substring(0, selectionBegin) + "^"
+ + newFileContents.substring(selectionBegin);
+ }
+
+ return newFileWithCaret;
+ }
+
+ protected String getCaretContext(String file, int offset) {
+ int windowSize = 20;
+ int begin = Math.max(0, offset - windowSize / 2);
+ int end = Math.min(file.length(), offset + windowSize / 2);
+
+ return "..." + file.substring(begin, offset) + "^" + file.substring(offset, end) + "...";
+ }
+
+ /**
+ * Very primitive line differ, intended for files where there are very minor changes
+ * (such as code completion apply-tests)
+ */
+ protected String getDiff(String before, String after) {
+ // Do line by line analysis
+ String[] beforeLines = before.split("\n");
+ String[] afterLines = after.split("\n");
+
+ int firstDelta = 0;
+ for (; firstDelta < Math.min(beforeLines.length, afterLines.length); firstDelta++) {
+ if (!beforeLines[firstDelta].equals(afterLines[firstDelta])) {
+ break;
+ }
+ }
+
+ if (firstDelta == beforeLines.length && firstDelta == afterLines.length) {
+ return "";
+ }
+
+ // Counts from the end of both arrays
+ int lastDelta = 0;
+ for (; lastDelta < Math.min(beforeLines.length, afterLines.length); lastDelta++) {
+ if (!beforeLines[beforeLines.length - 1 - lastDelta].equals(
+ afterLines[afterLines.length - 1 - lastDelta])) {
+ break;
+ }
+ }
+
+
+ boolean showBeforeWindow = firstDelta >= beforeLines.length - lastDelta;
+ boolean showAfterWindow = firstDelta >= afterLines.length - lastDelta;
+
+ StringBuilder sb = new StringBuilder();
+ if (showAfterWindow && firstDelta > 0) {
+ sb.append(" ");
+ sb.append(afterLines[firstDelta - 1]);
+ sb.append('\n');
+ }
+ for (int i = firstDelta; i < beforeLines.length - lastDelta; i++) {
+ sb.append("< ");
+ sb.append(beforeLines[i]);
+ sb.append('\n');
+ }
+ if (showAfterWindow && lastDelta < afterLines.length - 1) {
+ sb.append(" ");
+ sb.append(afterLines[afterLines.length - (lastDelta -1)]);
+ sb.append('\n');
+ }
+
+ sb.append("---\n");
+
+ if (showBeforeWindow && firstDelta > 0) {
+ sb.append(" ");
+ sb.append(beforeLines[firstDelta - 1]);
+ sb.append('\n');
+ }
+ for (int i = firstDelta; i < afterLines.length - lastDelta; i++) {
+ sb.append("> ");
+ sb.append(afterLines[i]);
+ sb.append('\n');
+ }
+ if (showBeforeWindow && lastDelta < beforeLines.length - 1) {
+ sb.append(" ");
+ sb.append(beforeLines[beforeLines.length - (lastDelta -1)]);
+ sb.append('\n');
+ }
+
+ return sb.toString();
+ }
+
+ protected String removeSessionData(String data) {
+ if (getProject() != null) {
+ data = data.replace(getProject().getName(), "PROJECTNAME");
+ }
+
+ return data;
+ }
+
+ public static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) {
+ if (hasChildren) {
+ return new ViewElementDescriptor(name, name, fqn, "", "", new AttributeDescriptor[0],
+ new AttributeDescriptor[0], new ElementDescriptor[1], false);
+ } else {
+ return new ViewElementDescriptor(name, fqn);
+ }
+ }
+
+ public static UiViewElementNode createNode(UiViewElementNode parent, String fqn,
+ boolean hasChildren) {
+ String name = fqn.substring(fqn.lastIndexOf('.') + 1);
+ ViewElementDescriptor descriptor = createDesc(name, fqn, hasChildren);
+ if (parent == null) {
+ // All node hierarchies should be wrapped inside a document node at the root
+ parent = new UiViewElementNode(createDesc("doc", "doc", true));
+ }
+ return (UiViewElementNode) parent.appendNewUiChild(descriptor);
+ }
+
+ public static UiViewElementNode createNode(String fqn, boolean hasChildren) {
+ return createNode(null, fqn, hasChildren);
+ }
+
+ protected String readTestFile(String relativePath, boolean expectExists) {
+ String path = "testdata" + File.separator + relativePath; //$NON-NLS-1$
+ InputStream stream =
+ AdtProjectTest.class.getResourceAsStream(path);
+ if (!expectExists && stream == null) {
+ return null;
+ }
+
+ assertNotNull(relativePath + " does not exist", stream);
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+ String xml = AdtPlugin.readFile(reader);
+ assertNotNull(xml);
+ assertTrue(xml.length() > 0);
+
+ // Remove any references to the project name such that we are isolated from
+ // that in golden file.
+ // Appears in strings.xml etc.
+ xml = removeSessionData(xml);
+
+ return xml;
+ }
+
+ protected void assertEqualsGolden(String basename, String actual) {
+ assertEqualsGolden(basename, actual, basename.substring(basename.lastIndexOf('.') + 1));
+ }
+
+ protected void assertEqualsGolden(String basename, String actual, String newExtension) {
+ String testName = getName();
+ if (testName.startsWith("test")) {
+ testName = testName.substring(4);
+ if (Character.isUpperCase(testName.charAt(0))) {
+ testName = Character.toLowerCase(testName.charAt(0)) + testName.substring(1);
+ }
+ }
+ String expectedName;
+ String extension = basename.substring(basename.lastIndexOf('.') + 1);
+ if (newExtension == null) {
+ newExtension = extension;
+ }
+ expectedName = basename.substring(0, basename.indexOf('.'))
+ + "-expected-" + testName + '.' + newExtension;
+ String expected = readTestFile(expectedName, false);
+ if (expected == null) {
+ File expectedPath = new File(
+ UPDATE_MISSING_FILES ? getTargetDir() : getTempDir(), expectedName);
+ AdtPlugin.writeFile(expectedPath, actual);
+ System.out.println("Expected - written to " + expectedPath + ":\n");
+ System.out.println(actual);
+ fail("Did not find golden file (" + expectedName + "): Wrote contents as "
+ + expectedPath);
+ } else {
+ if (!expected.equals(actual)) {
+ File expectedPath = new File(getTempDir(), expectedName);
+ File actualPath = new File(getTempDir(),
+ expectedName.replace("expected", "actual"));
+ AdtPlugin.writeFile(expectedPath, expected);
+ AdtPlugin.writeFile(actualPath, actual);
+ // Also update data dir with the current value
+ if (UPDATE_DIFFERENT_FILES) {
+ AdtPlugin.writeFile( new File(getTargetDir(), expectedName), actual);
+ }
+ System.out.println("The files differ: diff " + expectedPath + " "
+ + actualPath);
+ assertEquals("The files differ - see " + expectedPath + " versus " + actualPath,
+ expected, actual);
+ }
+ }
+ }
+
+ /** Get the location to write missing golden files to */
+ protected File getTargetDir() {
+ // Set $ADT_SDK_SOURCE_PATH to point to your git "sdk" directory; if done, then
+ // if you run a unit test which refers to a golden file which does not exist, it
+ // will be created directly into the test data directory and you can rerun the
+ // test
+ // and it should pass (after you verify that the golden file contains the correct
+ // result of course).
+ String sdk = System.getenv("ADT_SDK_SOURCE_PATH");
+ if (sdk != null) {
+ File sdkPath = new File(sdk);
+ if (sdkPath.exists()) {
+ File testData = new File(sdkPath, TEST_DATA_REL_PATH.replace('/',
+ File.separatorChar));
+ if (testData.exists()) {
+ return testData;
+ }
+ }
+ }
+ return getTempDir();
+ }
+
+ protected File getTempDir() {
+ if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
+ return new File("/tmp"); //$NON-NLS-1$
+ }
+
+ if (sTempDir == null) {
+ // On Windows, we don't want to pollute the temp folder (which is generally
+ // already incredibly busy). So let's create a temp folder for the results.
+
+ File base = new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+
+ Calendar c = Calendar.getInstance();
+ String name = String.format("adtTests_%1$tF_%1$tT", c).replace(':', '-'); //$NON-NLS-1$
+ File tmpDir = new File(base, name);
+ if (!tmpDir.exists() && tmpDir.mkdir()) {
+ sTempDir = tmpDir;
+ } else {
+ sTempDir = base;
+ }
+ }
+
+ return sTempDir;
+ }
+
+ /** Special editor context set on the model to be rendered */
+ protected static class TestLayoutEditor extends LayoutEditor {
+ private final IFile mFile;
+ private final IStructuredDocument mStructuredDocument;
+ private UiDocumentNode mUiRootNode;
+
+ public TestLayoutEditor(IFile file, IStructuredDocument structuredDocument,
+ UiDocumentNode uiRootNode) {
+ mFile = file;
+ mStructuredDocument = structuredDocument;
+ mUiRootNode = uiRootNode;
+ }
+
+ @Override
+ public IFile getInputFile() {
+ return mFile;
+ }
+
+ @Override
+ public IProject getProject() {
+ return mFile.getProject();
+ }
+
+ @Override
+ public IStructuredDocument getStructuredDocument() {
+ return mStructuredDocument;
+ }
+
+ @Override
+ public UiDocumentNode getUiRootNode() {
+ return mUiRootNode;
+ }
+
+ @Override
+ public void editorDirtyStateChanged() {
+ }
+
+ @Override
+ public IStructuredModel getModelForRead() {
+ IModelManager mm = StructuredModelManager.getModelManager();
+ if (mm != null) {
+ try {
+ return mm.getModelForRead(mFile);
+ } catch (Exception e) {
+ fail(e.toString());
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Stub class for project creation wizard.
+ * <p/>
+ * Created so project creation logic can be run without UI creation/manipulation.
+ */
+ public class StubProjectWizard extends NewProjectWizard {
+
+ private final String mProjectName;
+ private final IAndroidTarget mTarget;
+
+ public StubProjectWizard(String projectName, IAndroidTarget target) {
+ this.mProjectName = projectName;
+ this.mTarget = target;
+ }
+
+ /**
+ * Override parent to return stub page
+ */
+ @Override
+ protected NewProjectCreationPage createMainPage() {
+ return new StubProjectCreationPage(mProjectName, mTarget);
+ }
+
+ /**
+ * Override parent to return null page
+ */
+ @Override
+ protected NewTestProjectCreationPage createTestPage() {
+ return null;
+ }
+
+ /**
+ * Overrides parent to return dummy wizard container
+ */
+ @Override
+ public IWizardContainer getContainer() {
+ return new IWizardContainer() {
+
+ public IWizardPage getCurrentPage() {
+ return null;
+ }
+
+ public Shell getShell() {
+ return null;
+ }
+
+ public void showPage(IWizardPage page) {
+ // pass
+ }
+
+ public void updateButtons() {
+ // pass
+ }
+
+ public void updateMessage() {
+ // pass
+ }
+
+ public void updateTitleBar() {
+ // pass
+ }
+
+ public void updateWindowTitle() {
+ // pass
+ }
+
+ /**
+ * Executes runnable on current thread
+ */
+ public void run(boolean fork, boolean cancelable,
+ IRunnableWithProgress runnable)
+ throws InvocationTargetException, InterruptedException {
+ runnable.run(new NullProgressMonitor());
+ }
+
+ };
+ }
+ }
+
+ /**
+ * Stub class for project creation page.
+ * <p/>
+ * Returns canned responses for creating a sample project.
+ */
+ public class StubProjectCreationPage extends NewProjectCreationPage {
+
+ private final String mProjectName;
+ private final IAndroidTarget mTarget;
+
+ public StubProjectCreationPage(String projectName, IAndroidTarget target) {
+ super();
+ this.mProjectName = projectName;
+ this.mTarget = target;
+ setTestInfo(null);
+ }
+
+ @Override
+ public IMainInfo getMainInfo() {
+ return new IMainInfo() {
+ public String getProjectName() {
+ return mProjectName;
+ }
+
+ public String getPackageName() {
+ return "com.android.eclipse.tests";
+ }
+
+ public String getActivityName() {
+ return mProjectName;
+ }
+
+ public String getApplicationName() {
+ return mProjectName;
+ }
+
+ public boolean isNewProject() {
+ return true;
+ }
+
+ public String getSourceFolder() {
+ return "src";
+ }
+
+ public IPath getLocationPath() {
+ // Default location
+ return null;//new Path(mLocation);
+ }
+
+ public String getMinSdkVersion() {
+ return null;
+ }
+
+ public IAndroidTarget getSdkTarget() {
+ return mTarget;
+ }
+
+ public boolean isCreateActivity() {
+ return false;
+ }
+
+ public boolean useDefaultLocation() {
+ return true;
+ }
+
+ public IWorkingSet[] getSelectedWorkingSets() {
+ return new IWorkingSet[0];
+ }
+ };
+ }
+ }
+
+ public void testDummy() {
+ // This class contains shared test functionality for testcase subclasses,
+ // but without an actual test in the class JUnit complains (even if we make
+ // it abstract)
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoringTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoringTest.java
new file mode 100644
index 0000000..f822c62
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoringTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.FQCN_RELATIVE_LAYOUT;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.w3c.dom.Element;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ChangeLayoutRefactoringTest extends RefactoringTest {
+
+ public void testChangeLayout1a() throws Exception {
+ // Test a basic layout which performs some nesting -- tests basic grid layout conversion
+ checkRefactoring("sample1a.xml", true);
+ }
+
+ public void testChangeLayout1b() throws Exception {
+ // Same as 1a, but with different formatting to look for edit handling to for example
+ // remove a line that is made empty when its only attribute is removed
+ checkRefactoring("sample1b.xml", true);
+ }
+
+ public void testChangeLayout2() throws Exception {
+ // Test code which analyzes an embedded RelativeLayout
+ checkRefactoring("sample2.xml", true);
+ }
+
+ public void testChangeLayout3() throws Exception {
+ // Test handling of LinearLayout "weight" attributes on its children: the child with
+ // weight > 0 should fill and subsequent children attach on the bottom/right
+ checkRefactoring("sample3.xml", true);
+ }
+
+ public void testChangeLayout4() throws Exception {
+ checkRefactoring("sample4.xml", true);
+ }
+
+ public void testChangeLayout5() throws Exception {
+ // Test handling of LinearLayout "gravity" attributes on its children
+ checkRefactoring("sample5.xml", true);
+ }
+
+ public void testChangeLayout6() throws Exception {
+ // Check handling of the LinearLayout "baseline" attribute
+ checkRefactoring("sample6.xml", true);
+ }
+
+ private void checkRefactoring(String basename, boolean flatten) throws Exception {
+ IFile file = getLayoutFile(getProject(), basename);
+ TestContext info = setupTestContext(file, basename);
+ TestLayoutEditor layoutEditor = info.mLayoutEditor;
+ CanvasViewInfo rootView = info.mRootView;
+ Element element = info.mElement;
+
+ List<Element> selectedElements = Collections.singletonList(element);
+ ChangeLayoutRefactoring refactoring = new ChangeLayoutRefactoring(selectedElements,
+ layoutEditor);
+ refactoring.setFlatten(flatten);
+ refactoring.setType(FQCN_RELATIVE_LAYOUT);
+ refactoring.setRootView(rootView);
+
+ List<Change> changes = refactoring.computeChanges(new NullProgressMonitor());
+ checkEdits(basename, changes);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoringTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoringTest.java
new file mode 100644
index 0000000..80307d2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoringTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.FQCN_RADIO_BUTTON;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class ChangeViewRefactoringTest extends RefactoringTest {
+
+ public void testChangeView1() throws Exception {
+ checkRefactoring("sample1a.xml", FQCN_RADIO_BUTTON, "@+id/button1", "@+id/button6");
+ }
+
+ public void testChangeView2() throws Exception {
+ // Tests (1) updating references to the renamed id of the changed widgets
+ // (e.g. button3 is renamed to imageButton1 and layout references to button3
+ // must be updated), and (2) removal of attributes not available in the new type
+ // (the text property is removed since it is not available on the new widget
+ // type ImageButton)
+ checkRefactoring("sample2.xml", "android.widget.ImageButton",
+ "@+id/button3", "@+id/button5");
+ }
+
+ private void checkRefactoring(String basename, String newType,
+ String... ids) throws Exception {
+ assertTrue(ids.length > 0);
+
+ IFile file = getLayoutFile(getProject(), basename);
+ TestContext info = setupTestContext(file, basename);
+ TestLayoutEditor layoutEditor = info.mLayoutEditor;
+ List<Element> selectedElements = getElements(info.mElement, ids);
+
+ ChangeViewRefactoring refactoring = new ChangeViewRefactoring(selectedElements,
+ layoutEditor);
+ refactoring.setType(newType);
+
+ List<Change> changes = refactoring.computeChanges(new NullProgressMonitor());
+ checkEdits(basename, changes);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoringTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoringTest.java
new file mode 100644
index 0000000..da2a890
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoringTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.w3c.dom.Element;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExtractIncludeRefactoringTest extends RefactoringTest {
+ @Override
+ protected boolean testCaseNeedsUniqueProject() {
+ // Because some of these tests look at ALL layouts in the project
+ // to identify matches
+ return true;
+ }
+
+ public void testExtract1() throws Exception {
+ // Basic: Extract a single button
+ checkRefactoring("sample3.xml", "newlayout1", false, null, 2, false /* diffs */,
+ "@+id/button2");
+ }
+
+ public void testExtract2() throws Exception {
+ // Extract a couple of elements
+ checkRefactoring("sample3.xml", "newlayout2", false, null, 2, false /* diffs */,
+ "@+id/button2", "@+id/android_logo");
+ }
+
+ public void testExtract3() throws Exception {
+ // Test to make sure layout attributes are updated
+ checkRefactoring("sample2.xml", "newlayout3", false, null, 2, false /* diffs */,
+ "@+id/button3");
+ }
+
+ public void testExtract4() throws Exception {
+ // Tests extracting from -multiple- files (as well as with custom android namespace
+ // prefix)
+
+ // Make sure the variation-files exist
+ Map<IPath, String> extraFiles = new HashMap<IPath, String>();
+ extraFiles.put(getTestDataFile(getProject(), "sample3-variation1.xml",
+ "res/layout-land/sample3.xml").getProjectRelativePath(),
+ "sample3-variation1.xml");
+ extraFiles.put(getTestDataFile(getProject(), "sample3-variation2.xml",
+ "res/layout-xlarge-land/sample3.xml").getProjectRelativePath(),
+ "sample3-variation2.xml");
+
+ checkRefactoring("sample3.xml", "newlayout3", true, extraFiles, 4, false /* diffs */,
+ "@+id/android_logo");
+ }
+
+ public void testExtract5() throws Exception {
+ // Tests extracting from multiple files with -contiguous regions-.
+
+ // Make sure the variation-files exist
+ Map<IPath, String> extraFiles = new HashMap<IPath, String>();
+ extraFiles.put(getTestDataFile(getProject(), "sample3-variation1.xml",
+ "res/layout-land/sample3.xml").getProjectRelativePath(),
+ "sample3-variation1.xml");
+ extraFiles.put(getTestDataFile(getProject(), "sample3-variation2.xml",
+ "res/layout-xlarge-land/sample3.xml").getProjectRelativePath(),
+ "sample3-variation2.xml");
+
+ checkRefactoring("sample3.xml", "newlayout3", true, extraFiles, 4, false /* diffs */,
+ "@+id/android_logo", "@+id/button1");
+ }
+
+ public void testExtract6() throws Exception {
+ // Tests extracting from multiple files where the layouts are completely
+ // different/unrelated files
+
+ // Create the duplicate files
+ Map<IPath, String> extraFiles = new HashMap<IPath, String>();
+ extraFiles.put(getTestDataFile(getProject(), "sample1a.xml",
+ "res/layout/sample1a.xml").getProjectRelativePath(),
+ "sample1a.xml");
+ extraFiles.put(getTestDataFile(getProject(), "sample7.xml", "res/layout/sample7.xml")
+ .getProjectRelativePath(), "sample7.xml");
+ extraFiles.put(getTestDataFile(getProject(), "sample8.xml", "res/layout/sample8.xml")
+ .getProjectRelativePath(), "sample8.xml");
+
+ checkRefactoring("sample7.xml", "newlayout6", true, extraFiles, 4, true /* diffs */,
+ "@+id/linearLayout4");
+ }
+
+
+ private void checkRefactoring(String basename, String layoutName,
+ boolean replaceOccurrences, Map<IPath,String> extraFiles,
+ int expectedModifiedFileCount, boolean createDiffs, String... ids) throws Exception {
+ assertTrue(ids.length > 0);
+
+ IFile file = getLayoutFile(getProject(), basename);
+ TestContext info = setupTestContext(file, basename);
+ TestLayoutEditor layoutEditor = info.mLayoutEditor;
+ List<Element> selectedElements = getElements(info.mElement, ids);
+
+ ExtractIncludeRefactoring refactoring = new ExtractIncludeRefactoring(selectedElements,
+ layoutEditor);
+ refactoring.setLayoutName(layoutName);
+ refactoring.setReplaceOccurrences(replaceOccurrences);
+ List<Change> changes = refactoring.computeChanges(new NullProgressMonitor());
+
+ assertTrue(changes.size() >= 3);
+
+ Map<IPath,String> fileToGolden = new HashMap<IPath,String>();
+ IPath sourcePath = file.getProjectRelativePath();
+ fileToGolden.put(sourcePath, basename);
+ IPath newPath = sourcePath.removeLastSegments(1).append(layoutName + DOT_XML);
+ fileToGolden.put(newPath, layoutName + DOT_XML);
+ if (extraFiles != null) {
+ fileToGolden.putAll(extraFiles);
+ }
+
+ checkEdits(changes, fileToGolden, createDiffs);
+
+ int modifiedFileCount = 0;
+ for (Change change : changes) {
+ if (change instanceof TextFileChange) {
+ modifiedFileCount++;
+ }
+ }
+ assertEquals(expectedModifiedFileCount, modifiedFileCount);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoringTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoringTest.java
new file mode 100644
index 0000000..2802013
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoringTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import com.android.util.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.TextSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ExtractStyleRefactoringTest extends RefactoringTest {
+ @Override
+ protected boolean testCaseNeedsUniqueProject() {
+ return true;
+ }
+
+ public void testExtract1() throws Exception {
+ // Test extracting into a new style file
+ checkRefactoring("extractstyle1.xml", "newstyles.xml", "newstyle",
+ false /* removeExtracted */, false /* applyStyle */, null, 1, "@+id/button2");
+ }
+
+ public void testExtract1b() throws Exception {
+ // Extract and apply new style
+ checkRefactoring("extractstyle1.xml", "newstyles2.xml", "newstyle",
+ false /* removeExtracted */, true /* applyStyle */, null, 2, "@+id/button2");
+ }
+
+ public void testExtract1c() throws Exception {
+ // Extract and remove extracted
+ checkRefactoring("extractstyle1.xml", "newstyles3.xml", "newstyle",
+ true /* removeExtracted */, false /* applyStyle */, null, 2, "@+id/button2");
+ }
+
+ public void testExtract1d() throws Exception {
+ // Extract and apply style and remove extracted
+ checkRefactoring("extractstyle1.xml", "newstyles4.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2, "@+id/button2");
+ }
+
+ public void testExtract2() throws Exception {
+ getTestDataFile(getProject(), "navigationstyles.xml", "res/values/navigationstyles.xml");
+
+ // -Modify- the existing styles.xml file
+ checkRefactoring("extractstyle1.xml", "navigationstyles.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2, "@+id/button2");
+ }
+
+ public void testExtract3() throws Exception {
+ // Select multiple elements - overlap in values.
+ checkRefactoring("extractstyle1.xml", "newstyles4.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2,
+ "@+id/button1", "@+id/button2");
+ }
+
+ // This test fails for some reason - not in the refactoring (checked manually)
+ // but the DOM model returns null when run in a test context.
+ public void testExtract4() throws Exception {
+ // Test extracting on a single caret position over an attribute: Should extract
+ // just that one attribute
+ checkRefactoringByOffset("extractstyle1.xml", "newstyles5.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2,
+ "android:text^Color=\"#FF00FF\"", "android:text^Color=\"#FF00FF\"");
+ }
+
+ public void testExtract5() throws Exception {
+ // Test extracting on a range selection inside an element: should extract just
+ // the attributes that overlap the selection
+ checkRefactoringByOffset("extractstyle1.xml", "newstyles6.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2,
+ "android:^textSize=\"20pt",
+ "android:id=\"@+id/button1\" android:layout_a^lignParentBottom");
+ }
+
+ public void testExtract6() throws Exception {
+ // Test extracting on a single caret position which is not over any attributes:
+ checkRefactoringByOffset("extractstyle1.xml", "newstyles7.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 0,
+ "<Bu^tton", "<Bu^tton");
+ }
+
+ public void testExtract7() throws Exception {
+ // Verify that even with a different namespace prefix we end up with android:
+ // in the extracted style
+ checkRefactoring("extractstyle2.xml", "newstyles8.xml", "newstyle",
+ true /* removeExtracted */, true /* applyStyle */, null, 2,
+ "@+id/button1", "@+id/button2");
+ }
+
+ public void testExtract8() throws Exception {
+ // Test setting parent style
+ checkRefactoring("extractstyle1.xml", "newstyles3.xml", "newstyle",
+ true /* removeExtracted */, false /* applyStyle */, "android:Widget.Button",
+ 2, "@+id/button2");
+ }
+
+ // Check extract style on a selection of elements
+ private void checkRefactoring(String basename, String styleFileName, String newStyleName,
+ boolean removeExtracted, boolean applyStyle, String parentStyle,
+ int expectedModifiedFileCount, String... ids) throws Exception {
+ assertTrue(ids.length > 0);
+
+ IFile file = getLayoutFile(getProject(), basename);
+ TestContext info = setupTestContext(file, basename);
+ TestLayoutEditor layoutEditor = info.mLayoutEditor;
+ List<Element> selectedElements = getElements(info.mElement, ids);
+
+ // Open the file such that ModelManager.getExistingModelForRead() in DomUtilities
+ // will succeed
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();
+ IWorkbenchPage page = activeWorkbenchWindow.getActivePage();
+ IDE.openEditor(page, file);
+
+ ExtractStyleRefactoring refactoring = new ExtractStyleRefactoring(selectedElements,
+ layoutEditor);
+ checkRefactoring(basename, styleFileName, newStyleName, removeExtracted, applyStyle,
+ parentStyle, expectedModifiedFileCount, file, refactoring);
+ }
+
+ // Check extract style against a set of editor text locations
+ private void checkRefactoringByOffset(String basename, String styleFileName,
+ String newStyleName, boolean removeExtracted, boolean applyStyle,
+ String parentStyle,
+ int expectedModifiedFileCount, String beginCaretLocation, String endCaretLocation)
+ throws Exception {
+ IFile file = getLayoutFile(getProject(), basename);
+ int beginOffset = getCaretOffset(file, beginCaretLocation);
+ int endOffset = getCaretOffset(file, endCaretLocation);
+
+ TestContext info = setupTestContext(file, basename);
+ TestLayoutEditor layoutEditor = info.mLayoutEditor;
+
+ // Open the file such that ModelManager.getExistingModelForRead() in DomUtilities
+ // will succeed
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();
+ IWorkbenchPage page = activeWorkbenchWindow.getActivePage();
+ IDE.openEditor(page, file);
+
+ ITextSelection selection = new TextSelection(beginOffset, endOffset - beginOffset);
+ ExtractStyleRefactoring refactoring = new ExtractStyleRefactoring(file,
+ layoutEditor, selection, null);
+ checkRefactoring(basename, styleFileName, newStyleName, removeExtracted, applyStyle,
+ parentStyle, expectedModifiedFileCount, file, refactoring);
+ }
+
+ // Common test code used by the other two check methods
+ private void checkRefactoring(String basename, String styleFileName, String newStyleName,
+ boolean removeExtracted, boolean applyStyle, String parentStyle,
+ int expectedModifiedFileCount, IFile file,
+ ExtractStyleRefactoring refactoring) throws Exception {
+ refactoring.setStyleName(newStyleName);
+ refactoring.setApplyStyle(applyStyle);
+ refactoring.setRemoveExtracted(removeExtracted);
+ refactoring.setStyleFileName(styleFileName);
+ refactoring.setParent(parentStyle);
+
+ // Pick the attributes to extract -- for now everything (and where there are
+ // conflicting values, pick the first one)
+ Pair<Map<String, List<Attr>>, Set<Attr>> result = refactoring.getAvailableAttributes();
+ Map<String, List<Attr>> availableAttributes = result.getFirst();
+ Set<Attr> selected = result.getSecond();
+ List<Attr> chosenAttributes = new ArrayList<Attr>();
+ for (List<Attr> list : availableAttributes.values()) {
+ Collections.sort(list, new Comparator<Attr>() {
+ public int compare(Attr a1, Attr a2) {
+ return a1.getValue().compareTo(a2.getValue());
+ }
+ });
+ Attr attr = list.get(0);
+ if (selected.contains(attr)) {
+ chosenAttributes.add(attr);
+ }
+ }
+ refactoring.setChosenAttributes(chosenAttributes);
+
+ List<Change> changes = refactoring.computeChanges(new NullProgressMonitor());
+ assertEquals(expectedModifiedFileCount, changes.size());
+
+ Map<IPath,String> fileToGolden = new HashMap<IPath,String>();
+ IPath sourcePath = file.getProjectRelativePath();
+ fileToGolden.put(sourcePath, basename);
+ IPath newPath = refactoring.getStyleFile(getProject()).getProjectRelativePath();
+ fileToGolden.put(newPath, styleFileName);
+
+ checkEdits(changes, fileToGolden, true);
+
+ int modifiedFileCount = 0;
+ for (Change change : changes) {
+ if (change instanceof TextFileChange) {
+ modifiedFileCount++;
+ }
+ }
+ assertEquals(expectedModifiedFileCount, modifiedFileCount);
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistantTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistantTest.java
new file mode 100644
index 0000000..498f65a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistantTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.AndroidConstants.FD_RES_LAYOUT;
+import static com.android.sdklib.SdkConstants.FD_RES;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+
+public class RefactoringAssistantTest extends AdtProjectTest {
+ public void testAssistant1() throws Exception {
+ // "Extract String"
+ checkFixes("sample1a.xml", "<Button android:text=\"Fir^stButton\"");
+ }
+
+ public void testAssistant2() throws Exception {
+ // Visual refactoring operations
+ checkFixes("sample1a.xml", "<Bu^tton android:text");
+ }
+
+ public void testAssistant3() throws Exception {
+ // Negative test: ensure that we don't get completion items in other parts of the XML
+ checkFixes("sample1a.xml", "<Button andr^oid:text=\"FirstButton\"");
+ }
+
+ public void testAssistant4() throws Exception {
+ // Negative test: ensure that we don't offer extract string on a value that is
+ // already a resource
+ checkFixes("sample1a.xml", "android:id=\"@+id/Linea^rLayout2\"");
+ }
+
+ private void checkFixes(String name, String caretLocation)
+ throws Exception {
+ IProject project = getProject();
+ IFile file = getTestDataFile(project, name, FD_RES + "/" + FD_RES_LAYOUT + "/" + name);
+
+ // Determine the offset
+ String fileContent = AdtPlugin.readFile(file);
+ int caretDelta = caretLocation.indexOf("^");
+ assertTrue(caretLocation, caretDelta != -1);
+ String caretContext = caretLocation.substring(0, caretDelta)
+ + caretLocation.substring(caretDelta + "^".length());
+ int caretContextIndex = fileContent.indexOf(caretContext);
+ assertTrue("Caret content " + caretContext + " not found in file",
+ caretContextIndex != -1);
+ final int offset = caretContextIndex + caretDelta;
+
+
+ RefactoringAssistant refactoringAssistant = new RefactoringAssistant();
+
+ // Open file
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ assertNotNull(page);
+ IEditorPart editor = IDE.openEditor(page, file);
+ assertTrue(editor instanceof AndroidXmlEditor);
+ AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor;
+ final ISourceViewer viewer = layoutEditor.getStructuredSourceViewer();
+
+ IQuickAssistInvocationContext invocationContext = new IQuickAssistInvocationContext() {
+ public int getLength() {
+ return 0;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public ISourceViewer getSourceViewer() {
+ return viewer;
+ }
+ };
+ ICompletionProposal[] proposals = refactoringAssistant
+ .computeQuickAssistProposals(invocationContext);
+
+ if (proposals != null) {
+ for (ICompletionProposal proposal : proposals) {
+ assertNotNull(proposal.getAdditionalProposalInfo());
+ assertNotNull(proposal.getImage());
+ }
+ }
+
+ StringBuilder sb = new StringBuilder(1000);
+ sb.append("Quick assistant in " + name + " for " + caretLocation + ":\n");
+ if (proposals != null) {
+ for (ICompletionProposal proposal : proposals) {
+ sb.append(proposal.getDisplayString());
+ String help = proposal.getAdditionalProposalInfo();
+ if (help != null && help.trim().length() > 0) {
+ sb.append(" : ");
+ sb.append(help.replace('\n', ' '));
+ }
+ sb.append('\n');
+ }
+ } else {
+ sb.append("None found.\n");
+ }
+ assertEqualsGolden(name, sb.toString(), "txt");
+
+ // No "apply" test on these assists since they are interactive. Refactoring
+ // is tested elsewhere.
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringTest.java
new file mode 100644
index 0000000..4ca30d6
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
+import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
+
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Element;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@SuppressWarnings("restriction")
+public class RefactoringTest extends AdtProjectTest {
+
+ protected static Element findElementById(Element root, String id) {
+ if (id.equals(VisualRefactoring.getId(root))) {
+ return root;
+ }
+
+ for (Element child : DomUtilities.getChildren(root)) {
+ Element result = findElementById(child, id);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ protected static List<Element> getElements(Element root, String... ids) {
+ List<Element> selectedElements = new ArrayList<Element>();
+ for (String id : ids) {
+ Element element = findElementById(root, id);
+ assertNotNull(element);
+ selectedElements.add(element);
+ }
+ return selectedElements;
+ }
+
+ protected void checkEdits(String basename, List<Change> changes) throws BadLocationException,
+ IOException {
+ IDocument document = new Document();
+
+ String xml = readTestFile(basename, false);
+ if (xml == null) { // New file
+ xml = ""; //$NON-NLS-1$
+ }
+ document.set(xml);
+
+ for (Change change : changes) {
+ if (change instanceof TextFileChange) {
+ TextFileChange tf = (TextFileChange) change;
+ TextEdit edit = tf.getEdit();
+ if (edit instanceof MultiTextEdit) {
+ MultiTextEdit edits = (MultiTextEdit) edit;
+ edits.apply(document);
+ } else {
+ edit.apply(document);
+ }
+ } else {
+ System.out.println("Ignoring non-textfilechange in refactoring result");
+ }
+ }
+
+ String actual = document.get();
+ assertEqualsGolden(basename, actual);
+ }
+
+ protected void checkEdits(List<Change> changes,
+ Map<IPath, String> fileToGoldenName) throws BadLocationException {
+ checkEdits(changes, fileToGoldenName, false);
+ }
+
+ protected void checkEdits(List<Change> changes,
+ Map<IPath, String> fileToGoldenName, boolean createDiffs) throws BadLocationException {
+ for (Change change : changes) {
+ if (change instanceof TextFileChange) {
+ TextFileChange tf = (TextFileChange) change;
+ IFile file = tf.getFile();
+ assertNotNull(file);
+ IPath path = file.getProjectRelativePath();
+ String goldenName = fileToGoldenName.get(path);
+ assertNotNull("Not found: " + path.toString(), goldenName);
+
+ String xml = readTestFile(goldenName, false);
+ if (xml == null) { // New file
+ xml = ""; //$NON-NLS-1$
+ }
+ IDocument document = new Document();
+ document.set(xml);
+
+ String before = document.get();
+
+ TextEdit edit = tf.getEdit();
+ if (edit instanceof MultiTextEdit) {
+ MultiTextEdit edits = (MultiTextEdit) edit;
+ edits.apply(document);
+ } else {
+ edit.apply(document);
+ }
+
+ String actual = document.get();
+
+ if (createDiffs) {
+ // Use a diff as the golden file instead of the after
+ actual = getDiff(before, actual);
+ if (goldenName.endsWith(DOT_XML)) {
+ goldenName = goldenName.substring(0,
+ goldenName.length() - DOT_XML.length())
+ + ".diff";
+ }
+ }
+
+ assertEqualsGolden(goldenName, actual);
+ } else {
+ System.out.println("Ignoring non-textfilechange in refactoring result");
+ assertNull(change.getAffectedObjects());
+ }
+ }
+ }
+
+ protected UiViewElementNode createModel(UiViewElementNode parent, Element element) {
+ List<Element> children = DomUtilities.getChildren(element);
+ String fqcn = ANDROID_WIDGET_PREFIX + element.getTagName();
+ boolean hasChildren = children.size() > 0;
+ UiViewElementNode node = createNode(parent, fqcn, hasChildren);
+ node.setXmlNode(element);
+ for (Element child : children) {
+ createModel(node, child);
+ }
+
+ return node;
+ }
+
+ /**
+ * Builds up a ViewInfo hierarchy for the given model. This is done by
+ * reading .info dump files which record the exact pixel sizes of each
+ * ViewInfo object. These files are assumed to match up exactly with the
+ * model objects. This is done rather than rendering an actual layout
+ * hierarchy to insulate the test from pixel difference (in say font size)
+ * among platforms, as well as tying the test to particulars about relative
+ * sizes of things which may change with theme adjustments etc.
+ * <p>
+ * Each file can be generated by the dump method in the ViewHierarchy.
+ */
+ protected ViewInfo createInfos(UiElementNode model, String relativePath) {
+ String basename = relativePath.substring(0, relativePath.lastIndexOf('.') + 1);
+ String relative = basename + "info"; //$NON-NLS-1$
+ String info = readTestFile(relative, true);
+ // Parse the info file and build up a model from it
+ // Each line contains a new info.
+ // If indented it is a child of the parent.
+ String[] lines = info.split("\n"); //$NON-NLS-1$
+
+ // Iteration order for the info file should match exactly the UI model so
+ // we can just advance the line index sequentially as we traverse
+
+ return create(model, Arrays.asList(lines).iterator());
+ }
+
+ protected ViewInfo create(UiElementNode node, Iterator<String> lineIterator) {
+ // android.widget.LinearLayout [0,36,240,320]
+ Pattern pattern = Pattern.compile("(\\s*)(\\S+) \\[(\\d+),(\\d+),(\\d+),(\\d+)\\].*");
+ assertTrue(lineIterator.hasNext());
+ String description = lineIterator.next();
+ Matcher matcher = pattern.matcher(description);
+ assertTrue(matcher.matches());
+ //String indent = matcher.group(1);
+ //String fqcn = matcher.group(2);
+ String left = matcher.group(3);
+ String top = matcher.group(4);
+ String right = matcher.group(5);
+ String bottom = matcher.group(6);
+
+ ViewInfo view = new ViewInfo(node.getXmlNode().getLocalName(), node,
+ Integer.parseInt(left), Integer.parseInt(top),
+ Integer.parseInt(right), Integer.parseInt(bottom));
+
+ List<UiElementNode> childNodes = node.getUiChildren();
+ if (childNodes.size() > 0) {
+ List<ViewInfo> children = new ArrayList<ViewInfo>();
+ for (UiElementNode child : childNodes) {
+ children.add(create(child, lineIterator));
+ }
+ view.setChildren(children);
+ }
+
+ return view;
+ }
+
+ protected TestContext setupTestContext(IFile file, String relativePath) throws Exception {
+ IStructuredModel structuredModel = null;
+ org.w3c.dom.Document domDocument = null;
+ IStructuredDocument structuredDocument = null;
+ Element element = null;
+
+ try {
+ IModelManager modelManager = StructuredModelManager.getModelManager();
+ structuredModel = modelManager.getModelForRead(file);
+ if (structuredModel instanceof IDOMModel) {
+ IDOMModel domModel = (IDOMModel) structuredModel;
+ domDocument = domModel.getDocument();
+ element = domDocument.getDocumentElement();
+ structuredDocument = structuredModel.getStructuredDocument();
+ }
+ } finally {
+ if (structuredModel != null) {
+ structuredModel.releaseFromRead();
+ }
+ }
+
+ assertNotNull(structuredModel);
+ assertNotNull(domDocument);
+ assertNotNull(element);
+ assertNotNull(structuredDocument);
+ assertTrue(element instanceof IndexedRegion);
+
+ UiViewElementNode model = createModel(null, element);
+ ViewInfo info = createInfos(model, relativePath);
+ CanvasViewInfo rootView = CanvasViewInfo.create(info, true /* layoutlib5 */).getFirst();
+ TestLayoutEditor layoutEditor = new TestLayoutEditor(file, structuredDocument, null);
+
+ TestContext testInfo = createTestContext();
+ testInfo.mFile = file;
+ testInfo.mStructuredModel = structuredModel;
+ testInfo.mStructuredDocument = structuredDocument;
+ testInfo.mElement = element;
+ testInfo.mDomDocument = domDocument;
+ testInfo.mUiModel = model;
+ testInfo.mViewInfo = info;
+ testInfo.mRootView = rootView;
+ testInfo.mLayoutEditor = layoutEditor;
+
+ return testInfo;
+ }
+
+ protected TestContext createTestContext() {
+ return new TestContext();
+ }
+
+ protected static class TestContext {
+ protected IFile mFile;
+ protected IStructuredModel mStructuredModel;
+ protected IStructuredDocument mStructuredDocument;
+ protected org.w3c.dom.Document mDomDocument;
+ protected Element mElement;
+ protected UiViewElementNode mUiModel;
+ protected ViewInfo mViewInfo;
+ protected CanvasViewInfo mRootView;
+ protected TestLayoutEditor mLayoutEditor;
+ }
+
+ @Override
+ public void testDummy() {
+ // To avoid JUnit warning that this class contains no tests, even though
+ // this is an abstract class and JUnit shouldn't try
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoringTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoringTest.java
new file mode 100644
index 0000000..26d908b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoringTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.refactoring;
+
+import static com.android.ide.common.layout.LayoutConstants.FQCN_GESTURE_OVERLAY_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_LINEAR_LAYOUT;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class WrapInRefactoringTest extends RefactoringTest {
+
+ public void testWrapIn1() throws Exception {
+ // Test wrapping view: should indent view
+ checkRefactoring("sample3.xml", FQCN_LINEAR_LAYOUT, "@+id/button2");
+ }
+
+ public void testWrapIn2() throws Exception {
+ // Test wrapping the root: should move namespace
+ checkRefactoring("sample3.xml", FQCN_GESTURE_OVERLAY_VIEW, "@+id/newlinear");
+ }
+
+ public void testWrapIn3() throws Exception {
+ // Test wrap multiple adjacent elements - should wrap all as a unit
+ checkRefactoring("sample3.xml", FQCN_LINEAR_LAYOUT, "@+id/button2", "@+id/android_logo");
+ }
+
+ private void checkRefactoring(String basename, String fqcn, String... ids) throws Exception {
+ assertTrue(ids.length > 0);
+
+ IFile file = getLayoutFile(getProject(), basename);
+ TestContext info = setupTestContext(file, basename);
+ TestLayoutEditor layoutEditor = info.mLayoutEditor;
+ List<Element> selectedElements = getElements(info.mElement, ids);
+
+ WrapInRefactoring refactoring = new WrapInRefactoring(selectedElements,
+ layoutEditor);
+ refactoring.setType(fqcn);
+ List<Change> changes = refactoring.computeChanges(new NullProgressMonitor());
+ checkEdits(basename, changes);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror1.xml
new file mode 100644
index 0000000..d72f4ba
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror1.xml
@@ -0,0 +1,12 @@
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="50pt"
+ android:layout_marginLeft="50dp"
+ android:layout_marginBottom="50"
+ />
+</FrameLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror2.xml
new file mode 100644
index 0000000..b720daa
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror2.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="repeatedStyle1">
+ <item name="android:gravity">left</item>
+ </style>
+ <style name="repeatedStyle1">
+ <item name="android:gravity">bottom</item>
+ </style>
+</resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror3.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror3.xml
new file mode 100644
index 0000000..bc9c134
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror3.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="wrongAttribute">
+ <item name="nonexistent">5</item>
+ </style>
+</resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror4.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror4.xml
new file mode 100644
index 0000000..28dd467
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror4.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style>
+ <item />
+ </style>
+ <item></item>
+</resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror5.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror5.xml
new file mode 100644
index 0000000..ee89ac4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror5.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="test">
+ <item name="android:layout_width"></item>
+ </style>
+</resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror6.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror6.xml
new file mode 100644
index 0000000..e552ff7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror6.xml
@@ -0,0 +1,11 @@
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop=""
+ android:layout_marginLeft=''
+ />
+</FrameLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror7.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror7.xml
new file mode 100644
index 0000000..d47f4ae
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/aapterror7.xml
@@ -0,0 +1,10 @@
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+ <TextView
+ android:id=""
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
+</FrameLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion53.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion53.txt
new file mode 100644
index 0000000..3e44918
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion53.txt
@@ -0,0 +1,6 @@
+Code completion in anim1.xml for ^<set xmlns:
+<alpha />
+<rotate />
+<scale />
+<set ></set>
+<translate />
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion54.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion54.txt
new file mode 100644
index 0000000..f5e5cba
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion54.txt
@@ -0,0 +1,6 @@
+Code completion in anim1.xml for ^<translate android:id=:
+<alpha />
+<rotate />
+<scale />
+<set ></set>
+<translate />
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion55.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion55.txt
new file mode 100644
index 0000000..28b15d3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion55.txt
@@ -0,0 +1,16 @@
+Code completion in anim1.xml for android:^fromXDelta=:
+android:fromXDelta : [float, fraction]
+android:toXDelta : [float, fraction]
+android:fromYDelta : [float, fraction]
+android:toYDelta : [float, fraction]
+android:interpolator : Defines the interpolator used to smooth the animation movement in time. [reference]
+android:fillEnabled : When set to true, fillAfter is taken into account. [boolean]
+android:fillBefore : When set to true, the animation transformation is applied before the animation has started. [boolean]
+android:fillAfter : When set to true, the animation transformation is applied after the animation is over. [boolean]
+android:duration : Amount of time (in milliseconds) for the animation to run. [integer]
+android:startOffset : Delay in milliseconds before the animation runs, once start time is reached. [integer]
+android:repeatCount : Defines how many times the animation should repeat. [integer, enum]
+android:repeatMode : Defines the animation behavior when it reaches the end and the repeat count is greater than 0 or infinite. [enum]
+android:zAdjustment : Allows for an adjustment of the Z ordering of the content being animated for the duration of the animation. [enum]
+android:background : Special background behind animation. [reference, color]
+android:detachWallpaper : Special option for window animations: if this window is on top of a wallpaper, don't animate the wallpaper with it. [boolean]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion56.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion56.txt
new file mode 100644
index 0000000..632a9c5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion56.txt
@@ -0,0 +1,14 @@
+Code completion in anim1.xml for android:^fromAlpha=:
+android:fromAlpha : [float]
+android:toAlpha : [float]
+android:interpolator : Defines the interpolator used to smooth the animation movement in time. [reference]
+android:fillEnabled : When set to true, fillAfter is taken into account. [boolean]
+android:fillBefore : When set to true, the animation transformation is applied before the animation has started. [boolean]
+android:fillAfter : When set to true, the animation transformation is applied after the animation is over. [boolean]
+android:duration : Amount of time (in milliseconds) for the animation to run. [integer]
+android:startOffset : Delay in milliseconds before the animation runs, once start time is reached. [integer]
+android:repeatCount : Defines how many times the animation should repeat. [integer, enum]
+android:repeatMode : Defines the animation behavior when it reaches the end and the repeat count is greater than 0 or infinite. [enum]
+android:zAdjustment : Allows for an adjustment of the Z ordering of the content being animated for the duration of the animation. [enum]
+android:background : Special background behind animation. [reference, color]
+android:detachWallpaper : Special option for window animations: if this window is on top of a wallpaper, don't animate the wallpaper with it. [boolean]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion57.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion57.txt
new file mode 100644
index 0000000..9225dac
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1-expected-completion57.txt
@@ -0,0 +1,3 @@
+Code completion in anim1.xml for android:fromXDelta="100^%p":
+100% : <b>Fraction</b> - a percentage of the base size
+100%p : <b>Fraction</b> - a percentage relative to parent container
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1.xml
new file mode 100644
index 0000000..48fefc2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/anim1.xml
@@ -0,0 +1,20 @@
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:id="@+id/test1"
+ android:fromXDelta="100%p"
+ android:pivotY="60%p"
+ android:toXDelta="40%p"
+ android:toYDelta="33%p"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:startOffset="1000"
+ android:duration="1000" />
+ <alpha
+ android:id="@+id/test2"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:startOffset="3000"
+ android:duration="250"
+ android:fillBefore="true"
+ android:fillAfter="false"
+ />
+</set>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion58.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion58.txt
new file mode 100644
index 0000000..0759415
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion58.txt
@@ -0,0 +1,4 @@
+Code completion in animator1.xml for ^<set xmlns:
+<animator ></animator>
+<objectAnimator ></objectAnimator>
+<set ></set>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion59.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion59.txt
new file mode 100644
index 0000000..0702712
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion59.txt
@@ -0,0 +1,10 @@
+Code completion in animator1.xml for android:^duration="2000":
+android:propertyName : Name of the property being animated. [string]
+android:interpolator : Defines the interpolator used to smooth the animation movement in time. [reference]
+android:duration : Amount of time (in milliseconds) for the animation to run. [integer]
+android:startOffset : Delay in milliseconds before the animation runs, once start time is reached. [integer]
+android:repeatCount : Defines how many times the animation should repeat. [integer, enum]
+android:repeatMode : Defines the animation behavior when it reaches the end and the repeat count is greater than 0 or infinite. [enum]
+android:valueFrom : Value the animation starts from. [integer, float, color, dimension]
+android:valueTo : Value the animation animates to. [integer, float, color, dimension]
+android:valueType : The type of valueFrom and valueTo. [enum]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion60.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion60.txt
new file mode 100644
index 0000000..3e5e6b1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion60.txt
@@ -0,0 +1,3 @@
+Code completion in animator1.xml for android:propertyName="scal^eX"/>:
+scaleX : scale of the view in the x direction.
+scaleY : scale of the view in the y direction.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion61.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion61.txt
new file mode 100644
index 0000000..d4618c2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1-expected-completion61.txt
@@ -0,0 +1,18 @@
+Code completion in animator1.xml for android:interpolator="^@android:anim/bounce_interpolator":
+@android:anim/accelerate_decelerate_interpolator
+@android:anim/accelerate_interpolator
+@android:anim/decelerate_interpolator
+@android:anim/anticipate_interpolator
+@android:anim/overshoot_interpolator
+@android:anim/anticipate_overshoot_interpolator
+@android:anim/bounce_interpolator
+@android:anim/linear_interpolator
+@android:anim/cycle_interpolator
+@android:
+@anim/
+@animator/
+@color/
+@drawable/
+@layout/
+@string/
+@style/
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1.xml
new file mode 100644
index 0000000..bdf10dc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/animator1.xml
@@ -0,0 +1,27 @@
+<!-- Simple bounce animation -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="sequentially">
+ <set>
+ <objectAnimator
+ android:duration="2000"
+ android:valueTo="310"
+ android:propertyName="x"/>
+ <objectAnimator
+ android:duration="2000"
+ android:valueTo="130"
+ android:propertyName="y"
+ android:interpolator="@android:anim/bounce_interpolator"/>
+ <objectAnimator
+ android:duration="2000"
+ android:valueTo=".4"
+ android:propertyName="scaleX"/>
+ <objectAnimator
+ android:duration="2000"
+ android:valueTo=".4"
+ android:propertyName="scaleY"/>
+ </set>
+ <objectAnimator
+ android:duration="500"
+ android:valueTo="0"
+ android:propertyName="alpha"/>
+</set>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1-expected-applyCompletion15.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1-expected-applyCompletion15.diff
new file mode 100644
index 0000000..51a2cc9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1-expected-applyCompletion15.diff
@@ -0,0 +1,4 @@
+Code completion in broken1.xml for android:textColorHigh^ selecting android:textColorHighlight:
+< android:textColorHigh^
+---
+> android:textColorHighlight="^"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1-expected-completion20.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1-expected-completion20.txt
new file mode 100644
index 0000000..0a4c2f4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1-expected-completion20.txt
@@ -0,0 +1,2 @@
+Code completion in broken1.xml for android:textColorHigh^:
+android:textColorHighlight : Color of the text selection highlight. [reference, color]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1.xml
new file mode 100644
index 0000000..161b981
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken1.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <Button
+ android:text="@string/app_name"
+ android:textColorHigh
+ android:layout_marginLeft="@android:dimen/app_icon_size"
+ android:id="@+id/button1"
+ ></Button>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2-expected-applyCompletion16.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2-expected-applyCompletion16.diff
new file mode 100644
index 0000000..21437b9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2-expected-applyCompletion16.diff
@@ -0,0 +1,4 @@
+Code completion in broken2.xml for style=^ selecting "@android:":
+< style=^
+---
+> style="@android:^"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2-expected-completion21.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2-expected-completion21.txt
new file mode 100644
index 0000000..96e8408
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2-expected-completion21.txt
@@ -0,0 +1,5 @@
+Code completion in broken2.xml for style=^:
+"@android:"
+"@drawable/"
+"@layout/"
+"@string/"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2.xml
new file mode 100644
index 0000000..60644b9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken2.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <Button
+ android:text="@string/app_name"
+ android:layout_marginLeft="@android:dimen/app_icon_size"
+ style=
+ android:id="@+id/button1"
+ ></Button>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3-expected-applyCompletion14.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3-expected-applyCompletion14.diff
new file mode 100644
index 0000000..3e60eb9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3-expected-applyCompletion14.diff
@@ -0,0 +1,4 @@
+Code completion in broken3.xml for <EditT^ selecting EditText />:
+< <EditT^
+---
+> <EditText ^/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3-expected-completion19.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3-expected-completion19.txt
new file mode 100644
index 0000000..ccc6a4c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3-expected-completion19.txt
@@ -0,0 +1,2 @@
+Code completion in broken3.xml for <EditT^:
+EditText />
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3.xml
new file mode 100644
index 0000000..b8b1685
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/broken3.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <EditT
+ <Button
+ android:text="@string/app_name"
+ android:layout_marginLeft="@android:dimen/app_icon_size"
+ android:id="@+id/button1"
+ ></Button>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion45.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion45.txt
new file mode 100644
index 0000000..c799b80
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion45.txt
@@ -0,0 +1,2 @@
+Code completion in color1.xml for ^<selector:
+<selector ></selector> : Required. This must be the root element. Contains one or more <item> elements.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion46a.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion46a.txt
new file mode 100644
index 0000000..32f0066
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion46a.txt
@@ -0,0 +1,2 @@
+Code completion in color1.xml for ^<item android:
+<item /> : Drawable states.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion46b.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion46b.txt
new file mode 100644
index 0000000..7fcc5a9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1-expected-completion46b.txt
@@ -0,0 +1,16 @@
+Code completion in color1.xml for <item ^android:state_focused="true"/>:
+android:state_focused : State value for StateListDrawable, set when a view has input focus. [boolean]
+android:state_window_focused : State value for StateListDrawable, set when a view's window has input focus. [boolean]
+android:state_enabled : State value for StateListDrawable, set when a view is enabled. [boolean]
+android:state_checkable : State identifier indicating that the object <var>may</var> display a check mark. [boolean]
+android:state_checked : State identifier indicating that the object is currently checked. [boolean]
+android:state_selected : State value for StateListDrawable, set when a view (or one of its parents) is currently selected. [boolean]
+android:state_pressed : State value for StateListDrawable, set when the user is pressing down in a view. [boolean]
+android:state_activated : State value for StateListDrawable, set when a view or its parent has been "activated" meaning the user has currently marked it as being of interest. [boolean]
+android:state_active : State value for StateListDrawable. [boolean]
+android:state_single : State value for StateListDrawable. [boolean]
+android:state_first : State value for StateListDrawable. [boolean]
+android:state_middle : State value for StateListDrawable. [boolean]
+android:state_last : State value for StateListDrawable. [boolean]
+android:state_accelerated : State value for StateListDrawable, indicating that the Drawable is in a view that is hardware accelerated. [boolean]
+android:color : Hexadeximal color. Required. The color is specified with an RGB value and optional alpha channel. The value always begins with a pound (#) character and then followed by the Alpha-Red-Green-Blue information in one of the following formats: * RGB * ARGB * RRGGBB * AARRGGBB
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1.xml
new file mode 100644
index 0000000..a8482ab
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/color1.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"/>
+ <item />
+</selector>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-actual-applyCompletion1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-actual-applyCompletion1.xml
new file mode 100644
index 0000000..2413658
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-actual-applyCompletion1.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+<!--
+ This file deliberately contains errors - it represents partial keyboard
+ typing for interactive code completion
+-->
+ <TextView
+ android:layout_weight^="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@android:dimen/app_icon_size"
+ android:layout_marginLeft="50dp"
+ android:layout_marginBottom="50"
+ android:textColor="#000000"
+ style="@android:style/Widget.Button"
+ android:gravity="left|bottom"
+ android:text="@string/hello"
+ android:hint="hint" />
+ <FrameLayout android:foreground="@android:drawable/btn_default"></FrameLayout>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion1.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion1.diff
new file mode 100644
index 0000000..d656509
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion1.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for layout_w^idth="fill_parent" selecting android:layout_weight:
+< android:layout_w^idth="fill_parent"
+---
+> android:layout_weight^="fill_parent"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion10.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion10.diff
new file mode 100644
index 0000000..824fa25
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion10.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for <T^extView selecting TableLayout:
+< <T^extView
+---
+> <TableLayout^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion11a.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion11a.diff
new file mode 100644
index 0000000..7f4ec86
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion11a.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for ^<TextView selecting <RadioGroup ></RadioGroup>:
+< ^<TextView
+---
+> <RadioGroup ^></RadioGroup><TextView
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion11b.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion11b.diff
new file mode 100644
index 0000000..384c4a9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion11b.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for ^<TextView selecting <CheckBox />:
+< ^<TextView
+---
+> <CheckBox ^/><TextView
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion12.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion12.diff
new file mode 100644
index 0000000..a4b7231
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion12.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for btn_default">^</FrameLayout> selecting <FrameLayout ></FrameLayout>:
+< <FrameLayout android:foreground="@android:drawable/btn_default">^</FrameLayout>
+---
+> <FrameLayout android:foreground="@android:drawable/btn_default"><FrameLayout ^></FrameLayout></FrameLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion2.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion2.diff
new file mode 100644
index 0000000..a410606
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion2.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for layout_width="^fill_parent" selecting match_parent:
+< android:layout_width="^fill_parent"
+---
+> android:layout_width="match_parent"^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion3.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion3.diff
new file mode 100644
index 0000000..578f8ea
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion3.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for layout_width="fi^ll_parent" selecting fill_parent:
+< android:layout_width="fi^ll_parent"
+---
+> android:layout_width="fill_parent"^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion39.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion39.diff
new file mode 100644
index 0000000..577089b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion39.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for marginBottom="50"^ selecting android:maxEms:
+< android:layout_marginBottom="50"^
+---
+> android:layout_marginBottom="50" android:maxEms="^"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion4.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion4.diff
new file mode 100644
index 0000000..ebbba89
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion4.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for marginBottom="50^" selecting 50mm:
+< android:layout_marginBottom="50^"
+---
+> android:layout_marginBottom="50mm"^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion5.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion5.diff
new file mode 100644
index 0000000..ba7cb0b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion5.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for layout_marginLeft="50d^p" selecting 50dp:
+< android:layout_marginLeft="50d^p"
+---
+> android:layout_marginLeft="50dp"^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion6.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion6.diff
new file mode 100644
index 0000000..f1e6465
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion6.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for style="@android:^style/Widget.Button" selecting @android:drawable/:
+< style="@android:^style/Widget.Button"
+---
+> style="@android:drawable/^"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion7a.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion7a.diff
new file mode 100644
index 0000000..1a577db
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion7a.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for android:gravity="l^eft|bottom" selecting left:
+< android:gravity="l^eft|bottom"
+---
+> android:gravity="left^"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion7b.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion7b.diff
new file mode 100644
index 0000000..2560011
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion7b.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for android:gravity="left|b^ottom" selecting bottom:
+< android:gravity="left|b^ottom"
+---
+> android:gravity="left|bottom^"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion8.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion8.diff
new file mode 100644
index 0000000..655afc5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion8.diff
@@ -0,0 +1,2 @@
+Code completion in completion1.xml for layout_width^="fill_parent" selecting android:layout_width:
+No changes \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion9.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion9.diff
new file mode 100644
index 0000000..05656fb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-applyCompletion9.diff
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for layout_width=^"fill_parent" selecting "wrap_content":
+< android:layout_width=^"fill_parent"
+---
+> android:layout_width="wrap_content"^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion1.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion1.txt
new file mode 100644
index 0000000..949067a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion1.txt
@@ -0,0 +1,3 @@
+Code completion in completion1.xml for layout_w^idth="fill_parent":
+android:layout_width : Specifies the basic width of the view. [dimension, enum]
+android:layout_weight : [float]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion10.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion10.txt
new file mode 100644
index 0000000..68efdfb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion10.txt
@@ -0,0 +1,10 @@
+Code completion in completion1.xml for <T^extView:
+TabHost
+TabWidget
+TableLayout
+TableRow
+TextSwitcher
+TextView
+TimePicker
+ToggleButton
+TwoLineListItem
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion11.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion11.txt
new file mode 100644
index 0000000..9c1e396
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion11.txt
@@ -0,0 +1,60 @@
+Code completion in completion1.xml for ^<TextView:
+<AbsoluteLayout ></AbsoluteLayout>
+<AdapterViewFlipper ></AdapterViewFlipper>
+<AnalogClock />
+<AutoCompleteTextView />
+<Button />
+<CalendarView />
+<CheckBox />
+<CheckedTextView />
+<Chronometer />
+<DatePicker />
+<DialerFilter ></DialerFilter>
+<DigitalClock />
+<EditText />
+<ExpandableListView ></ExpandableListView>
+<FrameLayout ></FrameLayout>
+<Gallery />
+<GestureOverlayView /> : GestureOverlayView specific attributes.
+<GridView ></GridView>
+<HorizontalScrollView ></HorizontalScrollView>
+<ImageButton />
+<ImageSwitcher ></ImageSwitcher>
+<ImageView />
+<LinearLayout ></LinearLayout>
+<ListView ></ListView>
+<MediaController ></MediaController>
+<MultiAutoCompleteTextView />
+<NumberPicker />
+<ProgressBar />
+<QuickContactBadge />
+<RadioButton />
+<RadioGroup ></RadioGroup>
+<RatingBar />
+<RelativeLayout ></RelativeLayout>
+<ScrollView ></ScrollView>
+<SearchView ></SearchView>
+<SeekBar />
+<SlidingDrawer ></SlidingDrawer> : SlidingDrawer specific attributes.
+<Spinner />
+<StackView ></StackView>
+<SurfaceView />
+<TabHost ></TabHost>
+<TabWidget ></TabWidget>
+<TableLayout ></TableLayout>
+<TableRow ></TableRow>
+<TextSwitcher ></TextSwitcher>
+<TextView />
+<TimePicker />
+<ToggleButton />
+<TwoLineListItem />
+<VideoView />
+<View /> : Attributes that can be used with android.view.View or any of its subclasses.
+<ViewAnimator ></ViewAnimator>
+<ViewFlipper ></ViewFlipper>
+<ViewStub /> : A android.view.ViewStub lets you lazily include other XML layouts inside your application at runtime.
+<ViewSwitcher ></ViewSwitcher>
+<WebView />
+<ZoomButton />
+<ZoomControls />
+<include /> : Lets you statically include XML layouts inside other XML layouts.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion12.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion12.txt
new file mode 100644
index 0000000..fd41c10
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion12.txt
@@ -0,0 +1,60 @@
+Code completion in completion1.xml for btn_default">^</FrameLayout>:
+<AbsoluteLayout ></AbsoluteLayout>
+<AdapterViewFlipper ></AdapterViewFlipper>
+<AnalogClock />
+<AutoCompleteTextView />
+<Button />
+<CalendarView />
+<CheckBox />
+<CheckedTextView />
+<Chronometer />
+<DatePicker />
+<DialerFilter ></DialerFilter>
+<DigitalClock />
+<EditText />
+<ExpandableListView ></ExpandableListView>
+<FrameLayout ></FrameLayout>
+<Gallery />
+<GestureOverlayView /> : GestureOverlayView specific attributes.
+<GridView ></GridView>
+<HorizontalScrollView ></HorizontalScrollView>
+<ImageButton />
+<ImageSwitcher ></ImageSwitcher>
+<ImageView />
+<LinearLayout ></LinearLayout>
+<ListView ></ListView>
+<MediaController ></MediaController>
+<MultiAutoCompleteTextView />
+<NumberPicker />
+<ProgressBar />
+<QuickContactBadge />
+<RadioButton />
+<RadioGroup ></RadioGroup>
+<RatingBar />
+<RelativeLayout ></RelativeLayout>
+<ScrollView ></ScrollView>
+<SearchView ></SearchView>
+<SeekBar />
+<SlidingDrawer ></SlidingDrawer> : SlidingDrawer specific attributes.
+<Spinner />
+<StackView ></StackView>
+<SurfaceView />
+<TabHost ></TabHost>
+<TabWidget ></TabWidget>
+<TableLayout ></TableLayout>
+<TableRow ></TableRow>
+<TextSwitcher ></TextSwitcher>
+<TextView />
+<TimePicker />
+<ToggleButton />
+<TwoLineListItem />
+<VideoView />
+<View /> : Attributes that can be used with android.view.View or any of its subclasses.
+<ViewAnimator ></ViewAnimator>
+<ViewFlipper ></ViewFlipper>
+<ViewStub /> : A android.view.ViewStub lets you lazily include other XML layouts inside your application at runtime.
+<ViewSwitcher ></ViewSwitcher>
+<WebView />
+<ZoomButton />
+<ZoomControls />
+<include /> : Lets you statically include XML layouts inside other XML layouts.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion2.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion2.txt
new file mode 100644
index 0000000..136a6fe
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion2.txt
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for layout_width="^fill_parent":
+fill_parent
+match_parent
+wrap_content
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion3.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion3.txt
new file mode 100644
index 0000000..09c27ce
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion3.txt
@@ -0,0 +1,2 @@
+Code completion in completion1.xml for layout_width="fi^ll_parent":
+fill_parent
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion39.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion39.txt
new file mode 100644
index 0000000..d03f129
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion39.txt
@@ -0,0 +1,142 @@
+Code completion in completion1.xml for marginBottom="50"^:
+ style : A reference to a custom style [reference]
+ android:bufferType : Determines the minimum type that getText() will return. [enum]
+ android:text : Text to display. [string]
+ android:hint : Hint text to display when the text is empty. [string]
+ android:textColor : Text color. [reference, color]
+ android:textColorHighlight : Color of the text selection highlight. [reference, color]
+ android:textColorHint : Color of the hint text. [reference, color]
+ android:textAppearance : Base text color, typeface, size, and style. [reference]
+ android:textSize : Size of the text. [dimension]
+ android:textScaleX : Sets the horizontal scaling factor for the text. [float]
+ android:typeface : Typeface (normal, sans, serif, monospace) for the text. [enum]
+ android:textStyle : Style (bold, italic, bolditalic) for the text. [flag]
+ android:textColorLink : Text color for links. [reference, color]
+ android:cursorVisible : Makes the cursor visible (the default) or invisible. [boolean]
+ android:maxLines : Makes the TextView be at most this many lines tall. [integer]
+ android:maxHeight : Makes the TextView be at most this many pixels tall. [dimension]
+ android:lines : Makes the TextView be exactly this many lines tall. [integer]
+ android:height : Makes the TextView be exactly this many pixels tall. [dimension]
+ android:minLines : Makes the TextView be at least this many lines tall. [integer]
+ android:minHeight : Makes the TextView be at least this many pixels tall. [dimension]
+ android:maxEms : Makes the TextView be at most this many ems wide. [integer]
+ android:maxWidth : Makes the TextView be at most this many pixels wide. [dimension]
+ android:ems : Makes the TextView be exactly this many ems wide. [integer]
+ android:width : Makes the TextView be exactly this many pixels wide. [dimension]
+ android:minEms : Makes the TextView be at least this many ems wide. [integer]
+ android:minWidth : Makes the TextView be at least this many pixels wide. [dimension]
+ android:gravity : Specifies how to align the text by the view's x- and/or y-axis when the text is smaller than the view. [flag]
+ android:scrollHorizontally : Whether the text is allowed to be wider than the view (and therefore can be scrolled horizontally). [boolean]
+ android:password : Whether the characters of the field are displayed as password dots instead of themselves. * Deprecated: Use inputType instead. [boolean]
+ android:singleLine : Constrains the text to a single horizontally scrolling line instead of letting it wrap onto multiple lines, and advances focus instead of inserting a newline when you press the enter key. * Deprecated: This attribute is deprecated and is replaced by the textMultiLine flag in the inputType attribute. Use caution when altering existing layouts, as the default value of singeLine is false (multi-line mode), but if you specify any value for inputType, the default is single-line mode. (If both singleLine and inputType attributes are found, the inputType flags will override the value of singleLine.). [boolean]
+ android:enabled : Specifies whether the TextView is enabled or not. * Deprecated: Use state_enabled instead. [boolean]
+ android:selectAllOnFocus : If the text is selectable, select it all when the view takes focus instead of moving the cursor to the start or end. [boolean]
+ android:includeFontPadding : Leave enough room for ascenders and descenders instead of using the font ascent and descent strictly. [boolean]
+ android:maxLength : Set an input filter to constrain the text length to the specified number. [integer]
+ android:shadowColor : Place a shadow of the specified color behind the text. [color]
+ android:shadowDx : Horizontal offset of the shadow. [float]
+ android:shadowDy : Vertical offset of the shadow. [float]
+ android:shadowRadius : Radius of the shadow. [float]
+ android:autoLink : Controls whether links such as urls and email addresses are automatically found and converted to clickable links. [flag]
+ android:linksClickable : If set to false, keeps the movement method from being set to the link movement method even if autoLink causes links to be found. [boolean]
+ android:numeric : If set, specifies that this TextView has a numeric input method. * Deprecated: Use inputType instead. [flag]
+ android:digits : If set, specifies that this TextView has a numeric input method and that these specific characters are the ones that it will accept. [string]
+ android:phoneNumber : If set, specifies that this TextView has a phone number input method. * Deprecated: Use inputType instead. [boolean]
+ android:inputMethod : If set, specifies that this TextView should use the specified input method (specified by fully-qualified class name). * Deprecated: Use inputType instead. [string]
+ android:capitalize : If set, specifies that this TextView has a textual input method and should automatically capitalize what the user types. * Deprecated: Use inputType instead. [enum]
+ android:autoText : If set, specifies that this TextView has a textual input method and automatically corrects some common spelling errors. * Deprecated: Use inputType instead. [boolean]
+ android:editable : If set, specifies that this TextView has an input method. * Deprecated: Use inputType instead. [boolean]
+ android:freezesText : If set, the text view will include its current complete text inside of its frozen icicle in addition to meta-data such as the current cursor position. [boolean]
+ android:ellipsize : If set, causes words that are longer than the view is wide to be ellipsized instead of broken in the middle. [enum]
+ android:drawableTop : The drawable to be drawn above the text. [reference, color]
+ android:drawableBottom : The drawable to be drawn below the text. [reference, color]
+ android:drawableLeft : The drawable to be drawn to the left of the text. [reference, color]
+ android:drawableRight : The drawable to be drawn to the right of the text. [reference, color]
+ android:drawablePadding : The padding between the drawables and the text. [dimension]
+ android:lineSpacingExtra : Extra spacing between lines of text. [dimension]
+ android:lineSpacingMultiplier : Extra spacing between lines of text, as a multiplier. [float]
+ android:marqueeRepeatLimit : The number of times to repeat the marquee animation. [integer, enum]
+ android:inputType : The type of data being placed in a text field, used to help an input method decide how to let the user enter text. [flag]
+ android:imeOptions : Additional features you can enable in an IME associated with an editor to improve the integration with your application. [flag]
+ android:privateImeOptions : An addition content type description to supply to the input method attached to the text view, which is private to the implementation of the input method. [string]
+ android:imeActionLabel : Supply a value for EditorInfo.actionLabel used when an input method is connected to the text view. [string]
+ android:imeActionId : Supply a value for EditorInfo.actionId used when an input method is connected to the text view. [integer]
+ android:editorExtras : Reference to an "input-extras" XML resource containing additional data to supply to an input method, which is private to the implementation of the input method. [reference]
+ android:textSelectHandleLeft : Reference to a drawable that will be used to display a text selection anchor on the left side of a selection region. [reference]
+ android:textSelectHandleRight : Reference to a drawable that will be used to display a text selection anchor on the right side of a selection region. [reference]
+ android:textSelectHandle : Reference to a drawable that will be used to display a text selection anchor for positioning the cursor within text. [reference]
+ android:textEditPasteWindowLayout : The layout of the view that is displayed on top of the cursor to paste inside a TextEdit field. [reference]
+ android:textEditNoPasteWindowLayout : Variation of textEditPasteWindowLayout displayed when the clipboard is empty. [reference]
+ android:textEditSidePasteWindowLayout : Used instead of textEditPasteWindowLayout when the window is moved on the side of the insertion cursor because it would be clipped if it were positioned on top. [reference]
+ android:textEditSideNoPasteWindowLayout : Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. [reference]
+ android:textCursorDrawable : Reference to a drawable that will be drawn under the insertion cursor. [reference]
+ android:textIsSelectable : Indicates that the content of a non-editable text can be selected. [boolean]
+ android:id : Supply an identifier name for this view, to later retrieve it with View.findViewById() or Activity.findViewById(). [reference]
+ android:tag : Supply a tag for this view containing a String, to be retrieved later with View.getTag() or searched for with View.findViewWithTag() . [string]
+ android:scrollX : The initial horizontal scroll offset, in pixels. [dimension]
+ android:scrollY : The initial vertical scroll offset, in pixels. [dimension]
+ android:background : A drawable to use as the background. [reference, color]
+ android:padding : Sets the padding, in pixels, of all four edges. [dimension]
+ android:paddingLeft : Sets the padding, in pixels, of the left edge; see padding. [dimension]
+ android:paddingTop : Sets the padding, in pixels, of the top edge; see padding. [dimension]
+ android:paddingRight : Sets the padding, in pixels, of the right edge; see padding. [dimension]
+ android:paddingBottom : Sets the padding, in pixels, of the bottom edge; see padding. [dimension]
+ android:focusable : Boolean that controls whether a view can take focus. [boolean]
+ android:focusableInTouchMode : Boolean that controls whether a view can take focus while in touch mode. [boolean]
+ android:visibility : Controls the initial visibility of the view. [enum]
+ android:fitsSystemWindows : Boolean internal attribute to adjust view layout based on system windows such as the status bar. [boolean]
+ android:scrollbars : Defines which scrollbars should be displayed on scrolling or not. [flag]
+ android:scrollbarStyle : Controls the scrollbar style and position. [enum]
+ android:isScrollContainer : Set this if the view will serve as a scrolling container, meaing that it can be resized to shrink its overall window so that there will be space for an input method. [boolean]
+ android:fadeScrollbars : Defines whether to fade out scrollbars when they are not in use. [boolean]
+ android:scrollbarFadeDuration : Defines the delay in milliseconds that a scrollbar takes to fade out. [integer]
+ android:scrollbarDefaultDelayBeforeFade : Defines the delay in milliseconds that a scrollbar waits before fade out. [integer]
+ android:scrollbarSize : Sets the width of vertical scrollbars and height of horizontal scrollbars. [dimension]
+ android:scrollbarThumbHorizontal : Defines the horizontal scrollbar thumb drawable. [reference]
+ android:scrollbarThumbVertical : Defines the vertical scrollbar thumb drawable. [reference]
+ android:scrollbarTrackHorizontal : Defines the horizontal scrollbar track drawable. [reference]
+ android:scrollbarTrackVertical : Defines the vertical scrollbar track drawable. [reference]
+ android:scrollbarAlwaysDrawHorizontalTrack : Defines whether the horizontal scrollbar track should always be drawn. [boolean]
+ android:scrollbarAlwaysDrawVerticalTrack : Defines whether the vertical scrollbar track should always be drawn. [boolean]
+ android:fadingEdge : Defines which edges should be fadeded on scrolling. [flag]
+ android:fadingEdgeLength : Defines the length of the fading edges. [dimension]
+ android:nextFocusLeft : Defines the next view to give focus to when the next focus is FOCUS_LEFT. [reference]
+ android:nextFocusRight : Defines the next view to give focus to when the next focus is FOCUS_RIGHT If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+ android:nextFocusUp : Defines the next view to give focus to when the next focus is FOCUS_UP If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+ android:nextFocusDown : Defines the next view to give focus to when the next focus is FOCUS_DOWN If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+ android:nextFocusForward : Defines the next view to give focus to when the next focus is FOCUS_FORWARD If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+ android:clickable : Defines whether this view reacts to click events. [boolean]
+ android:longClickable : Defines whether this view reacts to long click events. [boolean]
+ android:saveEnabled : If unset, no state will be saved for this view when it is being frozen. [boolean]
+ android:filterTouchesWhenObscured : Specifies whether to filter touches when the view's window is obscured by another visible window. [boolean]
+ android:drawingCacheQuality : Defines the quality of translucent drawing caches. [enum]
+ android:keepScreenOn : Controls whether the view's window should keep the screen on while visible. [boolean]
+ android:duplicateParentState : When this attribute is set to true, the view gets its drawable state (focused, pressed, etc.) from its direct parent rather than from itself. [boolean]
+ android:minHeight : Defines the minimum height of the view.
+ android:minWidth : Defines the minimum width of the view.
+ android:soundEffectsEnabled : Boolean that controls whether a view should have sound effects enabled for events such as clicking and touching. [boolean]
+ android:hapticFeedbackEnabled : Boolean that controls whether a view should have haptic feedback enabled for events such as long presses. [boolean]
+ android:contentDescription : Defines text that briefly describes content of the view. [string]
+ android:onClick : Name of the method in this View's context to invoke when the view is clicked. [string]
+ android:overScrollMode : Defines over-scrolling behavior. [enum]
+ android:alpha : alpha property of the view, as a value between 0 (completely transparent) and 1 (completely opaque). [float]
+ android:translationX : translation in x of the view. [dimension]
+ android:translationY : translation in y of the view. [dimension]
+ android:transformPivotX : x location of the pivot point around which the view will rotate and scale. [dimension]
+ android:transformPivotY : y location of the pivot point around which the view will rotate and scale. [dimension]
+ android:rotation : rotation of the view, in degrees. [float]
+ android:rotationX : rotation of the view around the x axis, in degrees. [float]
+ android:rotationY : rotation of the view around the y axis, in degrees. [float]
+ android:scaleX : scale of the view in the x direction. [float]
+ android:scaleY : scale of the view in the y direction. [float]
+ android:verticalScrollbarPosition : Determines which side the vertical scroll bar should be placed on. [enum]
+ android:layerType : Specifies the type of layer backing this view. [enum]
+ android:layout_width : Specifies the basic width of the view. [dimension, enum]
+ android:layout_height : Specifies the basic height of the view. [dimension, enum]
+ android:layout_weight : [float]
+ android:layout_gravity : Standard gravity constant that a child can supply to its parent. [flag]
+ android:layout_margin : Specifies extra space on the left, top, right and bottom sides of this view. [dimension]
+ android:layout_marginLeft : Specifies extra space on the left side of this view. [dimension]
+ android:layout_marginTop : Specifies extra space on the top side of this view. [dimension]
+ android:layout_marginRight : Specifies extra space on the right side of this view. [dimension]
+ android:layout_marginBottom : Specifies extra space on the bottom side of this view. [dimension]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion4.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion4.txt
new file mode 100644
index 0000000..a9111eb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion4.txt
@@ -0,0 +1,7 @@
+Code completion in completion1.xml for marginBottom="50^":
+50dp : <b>Density-independent Pixels</b> - an abstract unit that is based on the physical density of the screen.
+50sp : <b>Scale-independent Pixels</b> - this is like the dp unit, but it is also scaled by the user's font size preference.
+50pt : <b>Points</b> - 1/72 of an inch based on the physical size of the screen.
+50mm : <b>Millimeters</b> - based on the physical size of the screen.
+50in : <b>Inches</b> - based on the physical size of the screen.
+50px : <b>Pixels</b> - corresponds to actual pixels on the screen. Not recommended.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion5.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion5.txt
new file mode 100644
index 0000000..6b1c993
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion5.txt
@@ -0,0 +1,2 @@
+Code completion in completion1.xml for layout_marginLeft="50d^p":
+50dp : <b>Density-independent Pixels</b> - an abstract unit that is based on the physical density of the screen.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion6.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion6.txt
new file mode 100644
index 0000000..0853a83
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion6.txt
@@ -0,0 +1,22 @@
+Code completion in completion1.xml for style="@android:^style/Widget.Button":
+@android:style/
+@android:anim/
+@android:animator/
+@android:array/
+@android:bool/
+@android:color/
+@android:declare-styleable/
+@android:dimen/
+@android:drawable/
+@android:fraction/
+@android:id/
+@android:integer/
+@android:interpolator/
+@android:layout/
+@android:menu/
+@android:mipmap/
+@android:plurals/
+@android:public/
+@android:raw/
+@android:string/
+@android:xml/
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion7a.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion7a.txt
new file mode 100644
index 0000000..cf373ad
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion7a.txt
@@ -0,0 +1,2 @@
+Code completion in completion1.xml for android:gravity="l^eft|bottom":
+left
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion7b.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion7b.txt
new file mode 100644
index 0000000..66276d6
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion7b.txt
@@ -0,0 +1,2 @@
+Code completion in completion1.xml for android:gravity="left|b^ottom":
+bottom
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion8.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion8.txt
new file mode 100644
index 0000000..bee7f93
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion8.txt
@@ -0,0 +1,2 @@
+Code completion in completion1.xml for layout_width^="fill_parent":
+android:layout_width : Specifies the basic width of the view. [dimension, enum]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion9.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion9.txt
new file mode 100644
index 0000000..ab6a0d2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1-expected-completion9.txt
@@ -0,0 +1,4 @@
+Code completion in completion1.xml for layout_width=^"fill_parent":
+"fill_parent"
+"match_parent"
+"wrap_content"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1.xml
new file mode 100644
index 0000000..d523eeb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion1.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+<!--
+ This file deliberately contains errors - it represents partial keyboard
+ typing for interactive code completion
+-->
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@android:dimen/app_icon_size"
+ android:layout_marginLeft="50dp"
+ android:layout_marginBottom="50"
+ android:textColor="#000000"
+ style="@android:style/Widget.Button"
+ android:gravity="left|bottom"
+ android:text="@string/hello"
+ android:hint="hint" />
+ <FrameLayout android:foreground="@android:drawable/btn_default"></FrameLayout>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13a.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13a.diff
new file mode 100644
index 0000000..13e224e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13a.diff
@@ -0,0 +1,4 @@
+Code completion in completion2.xml for gravity="left|bottom|^cen selecting fill_vertical:
+< <TextView android:gravity="left|bottom|^cen"></TextView>
+---
+> <TextView android:gravity="left|bottom|fill_vertical^"></TextView>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13b.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13b.diff
new file mode 100644
index 0000000..5d1b39e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13b.diff
@@ -0,0 +1,4 @@
+Code completion in completion2.xml for gravity="left|bottom|cen^ selecting center_horizontal:
+< <TextView android:gravity="left|bottom|cen^"></TextView>
+---
+> <TextView android:gravity="left|bottom|center_horizontal^"></TextView>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13c.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13c.diff
new file mode 100644
index 0000000..0c7e564
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-applyCompletion13c.diff
@@ -0,0 +1,4 @@
+Code completion in completion2.xml for gravity="left|bottom^|cen selecting bottom|fill_horizontal:
+< <TextView android:gravity="left|bottom^|cen"></TextView>
+---
+> <TextView android:gravity="left|bottom|fill_horizontal^"></TextView>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13a.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13a.txt
new file mode 100644
index 0000000..323bb78
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13a.txt
@@ -0,0 +1,13 @@
+Code completion in completion2.xml for gravity="left|bottom|^cen:
+top
+bottom
+left
+right
+center_vertical
+fill_vertical
+center_horizontal
+fill_horizontal
+center
+fill
+clip_vertical
+clip_horizontal
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13b.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13b.txt
new file mode 100644
index 0000000..8e6d4d8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13b.txt
@@ -0,0 +1,4 @@
+Code completion in completion2.xml for gravity="left|bottom|cen^:
+center_vertical
+center_horizontal
+center
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13c.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13c.txt
new file mode 100644
index 0000000..3e292ce
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2-expected-completion13c.txt
@@ -0,0 +1,12 @@
+Code completion in completion2.xml for gravity="left|bottom^|cen:
+bottom
+bottom|top
+bottom|right
+bottom|center_vertical
+bottom|fill_vertical
+bottom|center_horizontal
+bottom|fill_horizontal
+bottom|center
+bottom|fill
+bottom|clip_vertical
+bottom|clip_horizontal
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2.xml
new file mode 100644
index 0000000..0921674
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion2.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Test multiple pipes in the flag value -->
+ <TextView android:gravity="left|bottom|cen"></TextView>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3-expected-applyCompletion17.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3-expected-applyCompletion17.diff
new file mode 100644
index 0000000..14bb9ac
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3-expected-applyCompletion17.diff
@@ -0,0 +1,4 @@
+Code completion in completion3.xml for <EditText ^/> selecting android:textColorHighlight:
+< <EditText ^/>
+---
+> <EditText android:textColorHighlight="^"/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3-expected-applyCompletion18.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3-expected-applyCompletion18.diff
new file mode 100644
index 0000000..a751a3e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3-expected-applyCompletion18.diff
@@ -0,0 +1,4 @@
+Code completion in completion3.xml for <Button ^></Button> selecting android:paddingRight:
+< <Button ^></Button>
+---
+> <Button android:paddingRight="^"></Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3.xml
new file mode 100644
index 0000000..afb64b8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion3.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+ <EditText />
+ <Button ></Button>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion4-expected-completion22.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion4-expected-completion22.txt
new file mode 100644
index 0000000..c7170df
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion4-expected-completion22.txt
@@ -0,0 +1,2 @@
+Code completion in completion4.xml for <Button^:
+Button
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion4.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion4.xml
new file mode 100644
index 0000000..0047772
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion4.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <Button
+ android:text="@string/app_name"
+ android:layout_marginLeft="@android:dimen/app_icon_size"
+ android:id="@+id/button1"
+ ></Button>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion19.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion19.diff
new file mode 100644
index 0000000..16f42b2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion19.diff
@@ -0,0 +1,4 @@
+Code completion in completion5.xml for android:orientation='^' selecting horizontal:
+< android:orientation='^'
+---
+> android:orientation='horizontal'^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion20.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion20.diff
new file mode 100644
index 0000000..dd48af1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion20.diff
@@ -0,0 +1,4 @@
+Code completion in completion5.xml for android:layout_marginTop='50^dp' selecting 50pt:
+< android:layout_marginTop='50^dp'
+---
+> android:layout_marginTop='50pt'^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion21.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion21.diff
new file mode 100644
index 0000000..40e5bb1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion21.diff
@@ -0,0 +1,4 @@
+Code completion in completion5.xml for android:layout_width='^wrap_content' selecting match_parent:
+< android:layout_width='^wrap_content'
+---
+> android:layout_width='match_parent'^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion40.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion40.diff
new file mode 100644
index 0000000..00ffd0a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-applyCompletion40.diff
@@ -0,0 +1,4 @@
+Code completion in completion5.xml for android:id='@+id/button2'^ selecting android:maxWidth:
+< android:id='@+id/button2'^
+---
+> android:id='@+id/button2' android:maxWidth="^"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-completion40.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-completion40.txt
new file mode 100644
index 0000000..0b5aa7e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5-expected-completion40.txt
@@ -0,0 +1,142 @@
+Code completion in completion5.xml for android:id='@+id/button2'^:
+ style : A reference to a custom style [reference]
+ android:bufferType : Determines the minimum type that getText() will return. [enum]
+ android:text : Text to display. [string]
+ android:hint : Hint text to display when the text is empty. [string]
+ android:textColor : Text color. [reference, color]
+ android:textColorHighlight : Color of the text selection highlight. [reference, color]
+ android:textColorHint : Color of the hint text. [reference, color]
+ android:textAppearance : Base text color, typeface, size, and style. [reference]
+ android:textSize : Size of the text. [dimension]
+ android:textScaleX : Sets the horizontal scaling factor for the text. [float]
+ android:typeface : Typeface (normal, sans, serif, monospace) for the text. [enum]
+ android:textStyle : Style (bold, italic, bolditalic) for the text. [flag]
+ android:textColorLink : Text color for links. [reference, color]
+ android:cursorVisible : Makes the cursor visible (the default) or invisible. [boolean]
+ android:maxLines : Makes the TextView be at most this many lines tall. [integer]
+ android:maxHeight : Makes the TextView be at most this many pixels tall. [dimension]
+ android:lines : Makes the TextView be exactly this many lines tall. [integer]
+ android:height : Makes the TextView be exactly this many pixels tall. [dimension]
+ android:minLines : Makes the TextView be at least this many lines tall. [integer]
+ android:minHeight : Makes the TextView be at least this many pixels tall. [dimension]
+ android:maxEms : Makes the TextView be at most this many ems wide. [integer]
+ android:maxWidth : Makes the TextView be at most this many pixels wide. [dimension]
+ android:ems : Makes the TextView be exactly this many ems wide. [integer]
+ android:width : Makes the TextView be exactly this many pixels wide. [dimension]
+ android:minEms : Makes the TextView be at least this many ems wide. [integer]
+ android:minWidth : Makes the TextView be at least this many pixels wide. [dimension]
+ android:gravity : Specifies how to align the text by the view's x- and/or y-axis when the text is smaller than the view. [flag]
+ android:scrollHorizontally : Whether the text is allowed to be wider than the view (and therefore can be scrolled horizontally). [boolean]
+ android:password : Whether the characters of the field are displayed as password dots instead of themselves. * Deprecated: Use inputType instead. [boolean]
+ android:singleLine : Constrains the text to a single horizontally scrolling line instead of letting it wrap onto multiple lines, and advances focus instead of inserting a newline when you press the enter key. * Deprecated: This attribute is deprecated and is replaced by the textMultiLine flag in the inputType attribute. Use caution when altering existing layouts, as the default value of singeLine is false (multi-line mode), but if you specify any value for inputType, the default is single-line mode. (If both singleLine and inputType attributes are found, the inputType flags will override the value of singleLine.). [boolean]
+ android:enabled : Specifies whether the TextView is enabled or not. * Deprecated: Use state_enabled instead. [boolean]
+ android:selectAllOnFocus : If the text is selectable, select it all when the view takes focus instead of moving the cursor to the start or end. [boolean]
+ android:includeFontPadding : Leave enough room for ascenders and descenders instead of using the font ascent and descent strictly. [boolean]
+ android:maxLength : Set an input filter to constrain the text length to the specified number. [integer]
+ android:shadowColor : Place a shadow of the specified color behind the text. [color]
+ android:shadowDx : Horizontal offset of the shadow. [float]
+ android:shadowDy : Vertical offset of the shadow. [float]
+ android:shadowRadius : Radius of the shadow. [float]
+ android:autoLink : Controls whether links such as urls and email addresses are automatically found and converted to clickable links. [flag]
+ android:linksClickable : If set to false, keeps the movement method from being set to the link movement method even if autoLink causes links to be found. [boolean]
+ android:numeric : If set, specifies that this TextView has a numeric input method. * Deprecated: Use inputType instead. [flag]
+ android:digits : If set, specifies that this TextView has a numeric input method and that these specific characters are the ones that it will accept. [string]
+ android:phoneNumber : If set, specifies that this TextView has a phone number input method. * Deprecated: Use inputType instead. [boolean]
+ android:inputMethod : If set, specifies that this TextView should use the specified input method (specified by fully-qualified class name). * Deprecated: Use inputType instead. [string]
+ android:capitalize : If set, specifies that this TextView has a textual input method and should automatically capitalize what the user types. * Deprecated: Use inputType instead. [enum]
+ android:autoText : If set, specifies that this TextView has a textual input method and automatically corrects some common spelling errors. * Deprecated: Use inputType instead. [boolean]
+ android:editable : If set, specifies that this TextView has an input method. * Deprecated: Use inputType instead. [boolean]
+ android:freezesText : If set, the text view will include its current complete text inside of its frozen icicle in addition to meta-data such as the current cursor position. [boolean]
+ android:ellipsize : If set, causes words that are longer than the view is wide to be ellipsized instead of broken in the middle. [enum]
+ android:drawableTop : The drawable to be drawn above the text. [reference, color]
+ android:drawableBottom : The drawable to be drawn below the text. [reference, color]
+ android:drawableLeft : The drawable to be drawn to the left of the text. [reference, color]
+ android:drawableRight : The drawable to be drawn to the right of the text. [reference, color]
+ android:drawablePadding : The padding between the drawables and the text. [dimension]
+ android:lineSpacingExtra : Extra spacing between lines of text. [dimension]
+ android:lineSpacingMultiplier : Extra spacing between lines of text, as a multiplier. [float]
+ android:marqueeRepeatLimit : The number of times to repeat the marquee animation. [integer, enum]
+ android:inputType : The type of data being placed in a text field, used to help an input method decide how to let the user enter text. [flag]
+ android:imeOptions : Additional features you can enable in an IME associated with an editor to improve the integration with your application. [flag]
+ android:privateImeOptions : An addition content type description to supply to the input method attached to the text view, which is private to the implementation of the input method. [string]
+ android:imeActionLabel : Supply a value for EditorInfo.actionLabel used when an input method is connected to the text view. [string]
+ android:imeActionId : Supply a value for EditorInfo.actionId used when an input method is connected to the text view. [integer]
+ android:editorExtras : Reference to an "input-extras" XML resource containing additional data to supply to an input method, which is private to the implementation of the input method. [reference]
+ android:textSelectHandleLeft : Reference to a drawable that will be used to display a text selection anchor on the left side of a selection region. [reference]
+ android:textSelectHandleRight : Reference to a drawable that will be used to display a text selection anchor on the right side of a selection region. [reference]
+ android:textSelectHandle : Reference to a drawable that will be used to display a text selection anchor for positioning the cursor within text. [reference]
+ android:textEditPasteWindowLayout : The layout of the view that is displayed on top of the cursor to paste inside a TextEdit field. [reference]
+ android:textEditNoPasteWindowLayout : Variation of textEditPasteWindowLayout displayed when the clipboard is empty. [reference]
+ android:textEditSidePasteWindowLayout : Used instead of textEditPasteWindowLayout when the window is moved on the side of the insertion cursor because it would be clipped if it were positioned on top. [reference]
+ android:textEditSideNoPasteWindowLayout : Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. [reference]
+ android:textCursorDrawable : Reference to a drawable that will be drawn under the insertion cursor. [reference]
+ android:textIsSelectable : Indicates that the content of a non-editable text can be selected. [boolean]
+ android:id : Supply an identifier name for this view, to later retrieve it with View.findViewById() or Activity.findViewById(). [reference]
+ android:tag : Supply a tag for this view containing a String, to be retrieved later with View.getTag() or searched for with View.findViewWithTag() . [string]
+ android:scrollX : The initial horizontal scroll offset, in pixels. [dimension]
+ android:scrollY : The initial vertical scroll offset, in pixels. [dimension]
+ android:background : A drawable to use as the background. [reference, color]
+ android:padding : Sets the padding, in pixels, of all four edges. [dimension]
+ android:paddingLeft : Sets the padding, in pixels, of the left edge; see padding. [dimension]
+ android:paddingTop : Sets the padding, in pixels, of the top edge; see padding. [dimension]
+ android:paddingRight : Sets the padding, in pixels, of the right edge; see padding. [dimension]
+ android:paddingBottom : Sets the padding, in pixels, of the bottom edge; see padding. [dimension]
+ android:focusable : Boolean that controls whether a view can take focus. [boolean]
+ android:focusableInTouchMode : Boolean that controls whether a view can take focus while in touch mode. [boolean]
+ android:visibility : Controls the initial visibility of the view. [enum]
+ android:fitsSystemWindows : Boolean internal attribute to adjust view layout based on system windows such as the status bar. [boolean]
+ android:scrollbars : Defines which scrollbars should be displayed on scrolling or not. [flag]
+ android:scrollbarStyle : Controls the scrollbar style and position. [enum]
+ android:isScrollContainer : Set this if the view will serve as a scrolling container, meaing that it can be resized to shrink its overall window so that there will be space for an input method. [boolean]
+ android:fadeScrollbars : Defines whether to fade out scrollbars when they are not in use. [boolean]
+ android:scrollbarFadeDuration : Defines the delay in milliseconds that a scrollbar takes to fade out. [integer]
+ android:scrollbarDefaultDelayBeforeFade : Defines the delay in milliseconds that a scrollbar waits before fade out. [integer]
+ android:scrollbarSize : Sets the width of vertical scrollbars and height of horizontal scrollbars. [dimension]
+ android:scrollbarThumbHorizontal : Defines the horizontal scrollbar thumb drawable. [reference]
+ android:scrollbarThumbVertical : Defines the vertical scrollbar thumb drawable. [reference]
+ android:scrollbarTrackHorizontal : Defines the horizontal scrollbar track drawable. [reference]
+ android:scrollbarTrackVertical : Defines the vertical scrollbar track drawable. [reference]
+ android:scrollbarAlwaysDrawHorizontalTrack : Defines whether the horizontal scrollbar track should always be drawn. [boolean]
+ android:scrollbarAlwaysDrawVerticalTrack : Defines whether the vertical scrollbar track should always be drawn. [boolean]
+ android:fadingEdge : Defines which edges should be fadeded on scrolling. [flag]
+ android:fadingEdgeLength : Defines the length of the fading edges. [dimension]
+ android:nextFocusLeft : Defines the next view to give focus to when the next focus is FOCUS_LEFT. [reference]
+ android:nextFocusRight : Defines the next view to give focus to when the next focus is FOCUS_RIGHT If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+ android:nextFocusUp : Defines the next view to give focus to when the next focus is FOCUS_UP If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+ android:nextFocusDown : Defines the next view to give focus to when the next focus is FOCUS_DOWN If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+ android:nextFocusForward : Defines the next view to give focus to when the next focus is FOCUS_FORWARD If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+ android:clickable : Defines whether this view reacts to click events. [boolean]
+ android:longClickable : Defines whether this view reacts to long click events. [boolean]
+ android:saveEnabled : If unset, no state will be saved for this view when it is being frozen. [boolean]
+ android:filterTouchesWhenObscured : Specifies whether to filter touches when the view's window is obscured by another visible window. [boolean]
+ android:drawingCacheQuality : Defines the quality of translucent drawing caches. [enum]
+ android:keepScreenOn : Controls whether the view's window should keep the screen on while visible. [boolean]
+ android:duplicateParentState : When this attribute is set to true, the view gets its drawable state (focused, pressed, etc.) from its direct parent rather than from itself. [boolean]
+ android:minHeight : Defines the minimum height of the view.
+ android:minWidth : Defines the minimum width of the view.
+ android:soundEffectsEnabled : Boolean that controls whether a view should have sound effects enabled for events such as clicking and touching. [boolean]
+ android:hapticFeedbackEnabled : Boolean that controls whether a view should have haptic feedback enabled for events such as long presses. [boolean]
+ android:contentDescription : Defines text that briefly describes content of the view. [string]
+ android:onClick : Name of the method in this View's context to invoke when the view is clicked. [string]
+ android:overScrollMode : Defines over-scrolling behavior. [enum]
+ android:alpha : alpha property of the view, as a value between 0 (completely transparent) and 1 (completely opaque). [float]
+ android:translationX : translation in x of the view. [dimension]
+ android:translationY : translation in y of the view. [dimension]
+ android:transformPivotX : x location of the pivot point around which the view will rotate and scale. [dimension]
+ android:transformPivotY : y location of the pivot point around which the view will rotate and scale. [dimension]
+ android:rotation : rotation of the view, in degrees. [float]
+ android:rotationX : rotation of the view around the x axis, in degrees. [float]
+ android:rotationY : rotation of the view around the y axis, in degrees. [float]
+ android:scaleX : scale of the view in the x direction. [float]
+ android:scaleY : scale of the view in the y direction. [float]
+ android:verticalScrollbarPosition : Determines which side the vertical scroll bar should be placed on. [enum]
+ android:layerType : Specifies the type of layer backing this view. [enum]
+ android:layout_width : Specifies the basic width of the view. [dimension, enum]
+ android:layout_height : Specifies the basic height of the view. [dimension, enum]
+ android:layout_weight : [float]
+ android:layout_gravity : Standard gravity constant that a child can supply to its parent. [flag]
+ android:layout_margin : Specifies extra space on the left, top, right and bottom sides of this view. [dimension]
+ android:layout_marginLeft : Specifies extra space on the left side of this view. [dimension]
+ android:layout_marginTop : Specifies extra space on the top side of this view. [dimension]
+ android:layout_marginRight : Specifies extra space on the right side of this view. [dimension]
+ android:layout_marginBottom : Specifies extra space on the bottom side of this view. [dimension]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5.xml
new file mode 100644
index 0000000..757be8b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion5.xml
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='utf-8'?>
+<LinearLayout
+ xmlns:android='http://schemas.android.com/apk/res/android'
+ android:layout_width='match_parent'
+ android:orientation=''
+ android:layout_height='match_parent'>
+ <Button
+ android:text="what's up doc?"
+ android:id='@+id/button1'
+ android:layout_marginTop='50dp'
+ android:layout_width='wrap_content'
+ android:layout_height='wrap_content'></Button>
+ <Button
+ android:text="quote='"
+ android:id='@+id/button2'
+ android:layout_width='wrap_content'
+ android:layout_height='wrap_content'></Button>
+ <Button
+ android:text='quote="'
+ android:id='@+id/button2'
+ android:layout_width='wrap_content'
+ android:layout_height='wrap_content'></Button>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion6-expected-applyCompletion22.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion6-expected-applyCompletion22.diff
new file mode 100644
index 0000000..8ea8837
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion6-expected-applyCompletion22.diff
@@ -0,0 +1,4 @@
+Code completion in completion6.xml for android:orientation="^" selecting horizontal:
+< android:orientation="^"
+---
+> android:orientation="horizontal"^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion6.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion6.xml
new file mode 100644
index 0000000..55bfa0c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion6.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<LinearLayout
+ xmlns:android='http://schemas.android.com/apk/res/android'
+ android:layout_width='match_parent'
+ android:orientation=""
+ android:layout_height='match_parent'>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion7-expected-applyCompletion23.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion7-expected-applyCompletion23.diff
new file mode 100644
index 0000000..d874fc1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion7-expected-applyCompletion23.diff
@@ -0,0 +1,4 @@
+Code completion in completion7.xml for android:orientation="^ selecting horizontal:
+< android:orientation="^
+---
+> android:orientation="horizontal^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion7.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion7.xml
new file mode 100644
index 0000000..30afbaf
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion7.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<LinearLayout
+ xmlns:android='http://schemas.android.com/apk/res/android'
+ android:layout_width='match_parent'
+ android:orientation="
+ android:layout_height='match_parent'>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-applyCompletion41.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-applyCompletion41.diff
new file mode 100644
index 0000000..9a1b962
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-applyCompletion41.diff
@@ -0,0 +1,4 @@
+Code completion in completion8.xml for android:mar^="50dp" selecting android:layout_marginRight:
+< android:mar^="50dp"
+---
+> android:layout_marginRight^="50dp"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion41.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion41.txt
new file mode 100644
index 0000000..917d888
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion41.txt
@@ -0,0 +1,7 @@
+Code completion in completion8.xml for android:mar^="50dp":
+android:marqueeRepeatLimit : The number of times to repeat the marquee animation. [integer, enum]
+android:layout_margin : Specifies extra space on the left, top, right and bottom sides of this view. [dimension]
+android:layout_marginLeft : Specifies extra space on the left side of this view. [dimension]
+android:layout_marginTop : Specifies extra space on the top side of this view. [dimension]
+android:layout_marginRight : Specifies extra space on the right side of this view. [dimension]
+android:layout_marginBottom : Specifies extra space on the bottom side of this view. [dimension]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion42.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion42.txt
new file mode 100644
index 0000000..f51744d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion42.txt
@@ -0,0 +1,4 @@
+Code completion in completion8.xml for android:w^i="100":
+android:width : Makes the TextView be exactly this many pixels wide. [dimension]
+android:layout_width : Specifies the basic width of the view. [dimension, enum]
+android:layout_weight : [float]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion43.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion43.txt
new file mode 100644
index 0000000..622a2ba
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion43.txt
@@ -0,0 +1,7 @@
+Code completion in completion8.xml for mar^="60dp":
+android:marqueeRepeatLimit : The number of times to repeat the marquee animation. [integer, enum]
+android:layout_margin : Specifies extra space on the left, top, right and bottom sides of this view. [dimension]
+android:layout_marginLeft : Specifies extra space on the left side of this view. [dimension]
+android:layout_marginTop : Specifies extra space on the top side of this view. [dimension]
+android:layout_marginRight : Specifies extra space on the right side of this view. [dimension]
+android:layout_marginBottom : Specifies extra space on the bottom side of this view. [dimension]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion44.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion44.txt
new file mode 100644
index 0000000..6f7f2d3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8-expected-completion44.txt
@@ -0,0 +1,10 @@
+Code completion in completion8.xml for android:layo^ut_width="fill_parent":
+android:layout_width : Specifies the basic width of the view. [dimension, enum]
+android:layout_height : Specifies the basic height of the view. [dimension, enum]
+android:layout_weight : [float]
+android:layout_gravity : Standard gravity constant that a child can supply to its parent. [flag]
+android:layout_margin : Specifies extra space on the left, top, right and bottom sides of this view. [dimension]
+android:layout_marginLeft : Specifies extra space on the left side of this view. [dimension]
+android:layout_marginTop : Specifies extra space on the top side of this view. [dimension]
+android:layout_marginRight : Specifies extra space on the right side of this view. [dimension]
+android:layout_marginBottom : Specifies extra space on the bottom side of this view. [dimension]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8.xml
new file mode 100644
index 0000000..3f9d6b5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completion8.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:mar="50dp"
+ android:wi="100"
+ mar="60dp"
+ />
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion24a.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion24a.diff
new file mode 100644
index 0000000..4b88448
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion24a.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for android:textS^ize selecting android:textSelectHandleLeft:
+< <item name="android:textS^ize">17sp</item>
+---
+> <item name="android:textSelectHandleLeft"^>17sp</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion24b.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion24b.diff
new file mode 100644
index 0000000..acfd3ab
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion24b.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for 17^sp selecting 17mm:
+< <item name="android:textSize">17^sp</item>
+---
+> <item name="android:textSize">17mm^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion25.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion25.diff
new file mode 100644
index 0000000..ba7a1f2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion25.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for textColor">^@color/title_color</item> selecting @android::
+< <item name="android:textColor">^@color/title_color</item>
+---
+> <item name="android:textColor">@android:^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion26.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion26.diff
new file mode 100644
index 0000000..491363b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion26.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="android:shadowColor">@an^</item> selecting @android::
+< <item name="android:shadowColor">@an^</item>
+---
+> <item name="android:shadowColor">@android:^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion27.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion27.diff
new file mode 100644
index 0000000..1c999c7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion27.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="android:gravity">^ </item> selecting center_vertical:
+< <item name="android:gravity">^ </item>
+---
+> <item name="android:gravity">center_vertical^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion28.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion28.diff
new file mode 100644
index 0000000..fbfa7a8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion28.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="android:gravity"> ^</item> selecting left:
+< <item name="android:gravity"> ^</item>
+---
+> <item name="android:gravity"> left^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion29.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion29.diff
new file mode 100644
index 0000000..3e2cf0d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion29.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="gr^"> selecting android:gravity:
+< <item name="gr^">@color/title_color</item>
+---
+> <item name="android:gravity"^>@color/title_color</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion30.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion30.diff
new file mode 100644
index 0000000..5f79eaf
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion30.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="an^"> selecting android:animateOnClick:
+< <item name="an^">@color/title_color</item>
+---
+> <item name="android:animateOnClick"^>@color/title_color</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion31.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion31.diff
new file mode 100644
index 0000000..8f83183
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion31.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item ^></item> selecting name:
+< <item ^></item>
+---
+> <item name="^"></item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion32.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion32.diff
new file mode 100644
index 0000000..1a68f13
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion32.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="^"></item> selecting android:background:
+< <item name="^"></item>
+---
+> <item name="android:background"^></item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion33.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion33.diff
new file mode 100644
index 0000000..1a61da3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion33.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="android:allowSingleTap">^</item> selecting true:
+< <item name="android:allowSingleTap">^</item>
+---
+> <item name="android:allowSingleTap">true^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion34.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion34.diff
new file mode 100644
index 0000000..da4d30f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion34.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="android:alwaysDrawnWithCache">^ false </item> selecting true:
+< <item name="android:alwaysDrawnWithCache">^ false </item>
+---
+> <item name="android:alwaysDrawnWithCache">true^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion35.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion35.diff
new file mode 100644
index 0000000..4334c9f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion35.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="android:alwaysDrawnWithCache"> ^false </item> selecting true:
+< <item name="android:alwaysDrawnWithCache"> ^false </item>
+---
+> <item name="android:alwaysDrawnWithCache"> true^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion36.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion36.diff
new file mode 100644
index 0000000..2c9f547
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion36.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="android:alwaysDrawnWithCache"> f^alse </item> selecting false:
+< <item name="android:alwaysDrawnWithCache"> f^alse </item>
+---
+> <item name="android:alwaysDrawnWithCache"> false^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion37.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion37.diff
new file mode 100644
index 0000000..195e159
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion37.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for <item name="android:orientation">h^</item> selecting horizontal:
+< <item name="android:orientation">h^</item>
+---
+> <item name="android:orientation">horizontal^</item>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion38.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion38.diff
new file mode 100644
index 0000000..0e81810
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-applyCompletion38.diff
@@ -0,0 +1,4 @@
+Code completion in completionvalues1.xml for c^ selecting center:
+< c^
+---
+> center^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion23.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion23.txt
new file mode 100644
index 0000000..28cc9ba
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion23.txt
@@ -0,0 +1,7 @@
+Code completion in completionvalues1.xml for android:textS^ize:
+android:textScaleX : Sets the horizontal scaling factor for the text. [float]
+android:textSelectHandle : Reference to a drawable that will be used to display a text selection anchor for positioning the cursor within text. [reference]
+android:textSelectHandleLeft : Reference to a drawable that will be used to display a text selection anchor on the left side of a selection region. [reference]
+android:textSelectHandleRight : Reference to a drawable that will be used to display a text selection anchor on the right side of a selection region. [reference]
+android:textSize : Size of the text. [dimension]
+android:textStyle : Style (bold, italic, bolditalic) for the text. [flag]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion24.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion24.txt
new file mode 100644
index 0000000..c1de9ec
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion24.txt
@@ -0,0 +1,7 @@
+Code completion in completionvalues1.xml for 17^sp:
+17dp : <b>Density-independent Pixels</b> - an abstract unit that is based on the physical density of the screen.
+17sp : <b>Scale-independent Pixels</b> - this is like the dp unit, but it is also scaled by the user's font size preference.
+17pt : <b>Points</b> - 1/72 of an inch based on the physical size of the screen.
+17mm : <b>Millimeters</b> - based on the physical size of the screen.
+17in : <b>Inches</b> - based on the physical size of the screen.
+17px : <b>Pixels</b> - corresponds to actual pixels on the screen. Not recommended.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion25.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion25.txt
new file mode 100644
index 0000000..06f5a99
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion25.txt
@@ -0,0 +1,6 @@
+Code completion in completionvalues1.xml for textColor">^@color/title_color</item>:
+@android:
+@drawable/
+@layout/
+@string/
+@style/
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion26.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion26.txt
new file mode 100644
index 0000000..b1c541c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion26.txt
@@ -0,0 +1,2 @@
+Code completion in completionvalues1.xml for <item name="android:shadowColor">@an^</item>:
+@android:
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion27.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion27.txt
new file mode 100644
index 0000000..5c870fe
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion27.txt
@@ -0,0 +1,13 @@
+Code completion in completionvalues1.xml for <item name="android:gravity">^ </item>:
+top
+bottom
+left
+right
+center_vertical
+fill_vertical
+center_horizontal
+fill_horizontal
+center
+fill
+clip_vertical
+clip_horizontal
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion28.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion28.txt
new file mode 100644
index 0000000..183a7aa
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion28.txt
@@ -0,0 +1,13 @@
+Code completion in completionvalues1.xml for <item name="android:gravity"> ^</item>:
+top
+bottom
+left
+right
+center_vertical
+fill_vertical
+center_horizontal
+fill_horizontal
+center
+fill
+clip_vertical
+clip_horizontal
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion29.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion29.txt
new file mode 100644
index 0000000..0ad8ad2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion29.txt
@@ -0,0 +1,3 @@
+Code completion in completionvalues1.xml for <item name="gr^">:
+android:gravity : Specifies how to place the content of an object, both on the x- and y-axis, within the object itself. [flag]
+android:groupIndicator : Indicator shown beside the group View. [reference]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion30.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion30.txt
new file mode 100644
index 0000000..1f18826
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion30.txt
@@ -0,0 +1,8 @@
+Code completion in completionvalues1.xml for <item name="an^">:
+android:
+android:animateFirstView : Defines whether to animate the current View when the ViewAnimation is first displayed. [boolean]
+android:animateLayoutChanges : Defines whether changes in layout (caused by adding and removing items) should cause a LayoutTransition to run. [boolean]
+android:animateOnClick : Indicates whether the drawer should be opened/closed with an animation when the user clicks the handle. [boolean]
+android:animationCache : Defines whether layout animations should create a drawing cache for their children. [boolean]
+android:animationDuration : Sets how long a transition animation should run (in milliseconds) when layout has changed. [integer]
+android:animationResolution : Timeout between frames of animation in milliseconds [integer]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion31.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion31.txt
new file mode 100644
index 0000000..9044164
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion31.txt
@@ -0,0 +1,2 @@
+Code completion in completionvalues1.xml for <item ^></item>:
+name : The mandatory name used in referring to this item.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion32.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion32.txt
new file mode 100644
index 0000000..0ca6e41
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion32.txt
@@ -0,0 +1,293 @@
+Code completion in completionvalues1.xml for <item name="^"></item>:
+android:
+android:addStatesFromChildren : Sets whether this ViewGroup's drawable states also include its children's drawable states. [boolean]
+android:adjustViewBounds : Set this to true if you want the ImageView to adjust its bounds to preserve the aspect ratio of its drawable. [boolean]
+android:allowSingleTap : Indicates whether the drawer can be opened/closed by a single tap on the handle. [boolean]
+android:alpha : alpha property of the view, as a value between 0 (completely transparent) and 1 (completely opaque). [float]
+android:alwaysDrawnWithCache : Defines whether the ViewGroup should always draw its children using their drawing cache or not. [boolean]
+android:animateFirstView : Defines whether to animate the current View when the ViewAnimation is first displayed. [boolean]
+android:animateLayoutChanges : Defines whether changes in layout (caused by adding and removing items) should cause a LayoutTransition to run. [boolean]
+android:animateOnClick : Indicates whether the drawer should be opened/closed with an animation when the user clicks the handle. [boolean]
+android:animationCache : Defines whether layout animations should create a drawing cache for their children. [boolean]
+android:animationDuration : Sets how long a transition animation should run (in milliseconds) when layout has changed. [integer]
+android:animationResolution : Timeout between frames of animation in milliseconds [integer]
+android:autoLink : Controls whether links such as urls and email addresses are automatically found and converted to clickable links. [flag]
+android:autoStart : When true, automatically start animating [boolean]
+android:autoText : If set, specifies that this TextView has a textual input method and automatically corrects some common spelling errors. * Deprecated: Use inputType instead. [boolean]
+android:background : A drawable to use as the background. [reference, color]
+android:baseline : The offset of the baseline within this view. [dimension]
+android:baselineAlignBottom : If true, the image view will be baseline aligned with based on its bottom edge. [boolean]
+android:baselineAligned : When set to false, prevents the layout from aligning its children's baselines. [boolean]
+android:baselineAlignedChildIndex : When a linear layout is part of another layout that is baseline aligned, it can specify which of its children to baseline align to (that is, which child TextView). [integer]
+android:bottomOffset : Extra offset for the handle at the bottom of the SlidingDrawer. [dimension]
+android:bufferType : Determines the minimum type that getText() will return. [enum]
+android:button : Drawable used for the button graphic (e.g. checkbox, radio button, etc). [reference]
+android:cacheColorHint : Indicates that this list will always be drawn on top of solid, single-color opaque background. [color]
+android:calendarViewShown : Whether the calendar view is shown. [boolean]
+android:capitalize : If set, specifies that this TextView has a textual input method and should automatically capitalize what the user types. * Deprecated: Use inputType instead. [enum]
+android:checkMark : Drawable used for the check mark graphic. [reference]
+android:checked : Indicates the initial checked state of this button. [boolean]
+android:checkedButton : The id of the child radio button that should be checked by default within this radio group. [integer]
+android:childDivider : Drawable or color that is used as a divider for children. [reference, color]
+android:childIndicator : Indicator shown beside the child View. [reference]
+android:childIndicatorLeft : The left bound for a child's indicator. [dimension]
+android:childIndicatorRight : The right bound for a child's indicator. [dimension]
+android:choiceMode : Defines the choice behavior for the view. [enum]
+android:clickable : Defines whether this view reacts to click events. [boolean]
+android:clipChildren : Defines whether a child is limited to draw inside of its bounds or not. [boolean]
+android:clipToPadding : Defines whether the ViewGroup will clip its drawing surface so as to exclude the padding area. [boolean]
+android:collapseColumns : The zero-based index of the columns to collapse. [string]
+android:columnWidth : Specifies the fixed width for each column. [dimension]
+android:completionHint : Defines the hint displayed in the drop down menu. [string]
+android:completionHintView : Defines the hint view displayed in the drop down menu. [reference]
+android:completionThreshold : Defines the number of characters that the user must type before completion suggestions are displayed in a drop down menu. [integer]
+android:content : Identifier for the child that represents the drawer's content. [reference]
+android:contentDescription : Defines text that briefly describes content of the view. [string]
+android:cropToPadding : If true, the image will be cropped to fit within its padding. [boolean]
+android:cursorVisible : Makes the cursor visible (the default) or invisible. [boolean]
+android:dateTextAppearance : The text appearance for the calendar dates. [reference]
+android:descendantFocusability : Defines the relationship between the ViewGroup and its descendants when looking for a View to take focus. [enum]
+android:dial : [reference]
+android:digits : If set, specifies that this TextView has a numeric input method and that these specific characters are the ones that it will accept. [string]
+android:disabledAlpha : The alpha to apply to the indicator when disabled. [float]
+android:divider : Drawable to use as a vertical divider between buttons.
+android:dividerHeight : Height of the divider. [dimension]
+android:dividerPadding : Size of padding on either end of a divider. [dimension]
+android:drawSelectorOnTop : When set to true, the selector will be drawn over the selected item. [boolean]
+android:drawableBottom : The drawable to be drawn below the text. [reference, color]
+android:drawableLeft : The drawable to be drawn to the left of the text. [reference, color]
+android:drawablePadding : The padding between the drawables and the text. [dimension]
+android:drawableRight : The drawable to be drawn to the right of the text. [reference, color]
+android:drawableTop : The drawable to be drawn above the text. [reference, color]
+android:drawingCacheQuality : Defines the quality of translucent drawing caches. [enum]
+android:dropDownAnchor : View to anchor the auto-complete dropdown to. [reference]
+android:dropDownHeight : Specifies the basic height of the dropdown. [dimension, enum]
+android:dropDownHorizontalOffset : Horizontal offset from the spinner widget for positioning the dropdown in spinnerMode="dropdown". [dimension]
+android:dropDownSelector : List selector to use for spinnerMode="dropdown" display. [reference, color]
+android:dropDownVerticalOffset : Vertical offset from the spinner widget for positioning the dropdown in spinnerMode="dropdown". [dimension]
+android:dropDownWidth : Width of the dropdown in spinnerMode="dropdown". [dimension, enum]
+android:duplicateParentState : When this attribute is set to true, the view gets its drawable state (focused, pressed, etc.) from its direct parent rather than from itself. [boolean]
+android:editable : If set, specifies that this TextView has an input method. * Deprecated: Use inputType instead. [boolean]
+android:editorExtras : Reference to an "input-extras" XML resource containing additional data to supply to an input method, which is private to the implementation of the input method. [reference]
+android:ellipsize : If set, causes words that are longer than the view is wide to be ellipsized instead of broken in the middle. [enum]
+android:ems : Makes the TextView be exactly this many ems wide. [integer]
+android:enabled : Specifies whether the TextView is enabled or not. * Deprecated: Use state_enabled instead. [boolean]
+android:endYear : The last year (inclusive), for example "2010". [integer]
+android:entries : Reference to an array resource that will populate the Spinner. [reference]
+android:eventsInterceptionEnabled : Defines whether the overlay should intercept the motion events when a gesture is recognized. [boolean]
+android:fadeDuration : Duration, in milliseconds, of the fade out effect after the user is done drawing a gesture. [integer]
+android:fadeEnabled : Defines whether the gesture will automatically fade out after being recognized. [boolean]
+android:fadeOffset : Time, in milliseconds, to wait before the gesture fades out after the user is done drawing it. [integer]
+android:fadeScrollbars : Defines whether to fade out scrollbars when they are not in use. [boolean]
+android:fadingEdge : Defines which edges should be fadeded on scrolling. [flag]
+android:fadingEdgeLength : Defines the length of the fading edges. [dimension]
+android:fastScrollAlwaysVisible : When set to true, the list will always show the fast scroll interface. [boolean]
+android:fastScrollEnabled : Enables the fast scroll thumb that can be dragged to quickly scroll through the list. [boolean]
+android:fillViewport : Defines whether the scrollview should stretch its content to fill the viewport. [boolean]
+android:filterTouchesWhenObscured : Specifies whether to filter touches when the view's window is obscured by another visible window. [boolean]
+android:firstDayOfWeek : The first day of week according to java.util.Calendar. [integer]
+android:fitsSystemWindows : Boolean internal attribute to adjust view layout based on system windows such as the status bar. [boolean]
+android:flingable : @hide Whether the number picker supports fligning. [boolean]
+android:flipInterval : [integer]
+android:focusable : Boolean that controls whether a view can take focus. [boolean]
+android:focusableInTouchMode : Boolean that controls whether a view can take focus while in touch mode. [boolean]
+android:focusedMonthDateColor : The color for the dates of the selected month. [reference, color]
+android:footerDividersEnabled : When set to false, the ListView will not draw the divider before each footer view. [boolean]
+android:foreground : Defines the drawable to draw over the content. [reference, color]
+android:foregroundGravity : Defines the gravity to apply to the foreground drawable. [flag]
+android:foregroundInsidePadding : Defines whether the foreground drawable should be drawn inside the padding. [boolean]
+android:format : Format string: if specified, the Chronometer will display this string, with the first "%s" replaced by the current timer value in "MM:SS" or "H:MM:SS" form. [string]
+android:freezesText : If set, the text view will include its current complete text inside of its frozen icicle in addition to meta-data such as the current cursor position. [boolean]
+android:gestureColor : Color used to draw a gesture. [color]
+android:gestureStrokeAngleThreshold : Minimum curve angle a stroke must contain before it is recognized as a gesture. [float]
+android:gestureStrokeLengthThreshold : Minimum length of a stroke before it is recognized as a gesture. [float]
+android:gestureStrokeSquarenessThreshold : Squareness threshold of a stroke before it is recognized as a gesture. [float]
+android:gestureStrokeType : Defines the type of strokes that define a gesture. [enum]
+android:gestureStrokeWidth : Width of the stroke used to draw the gesture. [float]
+android:gravity : Specifies how to place the content of an object, both on the x- and y-axis, within the object itself. [flag]
+android:groupIndicator : Indicator shown beside the group View. [reference]
+android:hand_hour : [reference]
+android:hand_minute : [reference]
+android:handle : Identifier for the child that represents the drawer's handle. [reference]
+android:hapticFeedbackEnabled : Boolean that controls whether a view should have haptic feedback enabled for events such as long presses. [boolean]
+android:headerDividersEnabled : When set to false, the ListView will not draw the divider after each header view. [boolean]
+android:height : Makes the TextView be exactly this many pixels tall. [dimension]
+android:hint : Hint text to display when the text is empty. [string]
+android:horizontalSpacing : Defines the default horizontal spacing between columns. [dimension]
+android:iconifiedByDefault : The default state of the SearchView. [boolean]
+android:id : [reference]. * Required.
+android:ignoreGravity : Indicates what view should not be affected by gravity. [reference]
+android:imeActionId : Supply a value for EditorInfo.actionId used when an input method is connected to the text view. [integer]
+android:imeActionLabel : Supply a value for EditorInfo.actionLabel used when an input method is connected to the text view. [string]
+android:imeOptions : Additional features you can enable in an IME associated with an editor to improve the integration with your application. [flag]
+android:inAnimation : Identifier for the animation to use when a view is shown. [reference]
+android:includeFontPadding : Leave enough room for ascenders and descenders instead of using the font ascent and descent strictly. [boolean]
+android:indeterminate : Allows to enable the indeterminate mode. [boolean]
+android:indeterminateBehavior : Defines how the indeterminate mode should behave when the progress reaches max. [enum]
+android:indeterminateDrawable : Drawable used for the indeterminate mode. [reference]
+android:indeterminateDuration : Duration of the indeterminate animation. [integer]
+android:indeterminateOnly : Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). [boolean]
+android:indicatorLeft : The left bound for an item's indicator. [dimension]
+android:indicatorRight : The right bound for an item's indicator. [dimension]
+android:inflatedId : Overrides the id of the inflated View with this value. [reference]
+android:inputMethod : If set, specifies that this TextView should use the specified input method (specified by fully-qualified class name). * Deprecated: Use inputType instead. [string]
+android:inputType : The type of data being placed in a text field, used to help an input method decide how to let the user enter text. [flag]
+android:interpolator : [reference]
+android:isIndicator : Whether this rating bar is an indicator (and non-changeable by the user). [boolean]
+android:isScrollContainer : Set this if the view will serve as a scrolling container, meaing that it can be resized to shrink its overall window so that there will be space for an input method. [boolean]
+android:keepScreenOn : Controls whether the view's window should keep the screen on while visible. [boolean]
+android:layerType : Specifies the type of layer backing this view. [enum]
+layout : [reference]. * Required.
+android:layoutAnimation : Defines the layout animation to use the first time the ViewGroup is laid out. [reference]
+android:lineSpacingExtra : Extra spacing between lines of text. [dimension]
+android:lineSpacingMultiplier : Extra spacing between lines of text, as a multiplier. [float]
+android:lines : Makes the TextView be exactly this many lines tall. [integer]
+android:linksClickable : If set to false, keeps the movement method from being set to the link movement method even if autoLink causes links to be found. [boolean]
+android:listSelector : Drawable used to indicate the currently selected item in the list. [reference, color]
+android:longClickable : Defines whether this view reacts to long click events. [boolean]
+android:loopViews : Defines whether the animator loops to the first view once it has reached the end of the list. [boolean]
+android:marqueeRepeatLimit : The number of times to repeat the marquee animation. [integer, enum]
+android:max : Defines the maximum value the progress can take. [integer]
+android:maxDate : The minimal date shown by this calendar view in mm/dd/yyyy format. [string]
+android:maxEms : Makes the TextView be at most this many ems wide. [integer]
+android:maxHeight : An optional argument to supply a maximum height for this view. [dimension]
+android:maxLength : Set an input filter to constrain the text length to the specified number. [integer]
+android:maxLines : Makes the TextView be at most this many lines tall. [integer]
+android:maxWidth : An optional argument to supply a maximum width for this view. [dimension]
+android:measureAllChildren : Determines whether to measure all children or just those in the VISIBLE or INVISIBLE state when measuring. [boolean]
+android:measureWithLargestChild : When set to true, all children with a weight will be considered having the minimum size of the largest child. [boolean]
+android:minDate : The minimal date shown by this calendar view in mm/dd/yyyy format. [string]
+android:minEms : Makes the TextView be at least this many ems wide. [integer]
+android:minHeight : Defines the minimum height of the view.
+android:minLines : Makes the TextView be at least this many lines tall. [integer]
+android:minWidth : Defines the minimum width of the view.
+android:mode : [enum]
+android:nextFocusDown : Defines the next view to give focus to when the next focus is FOCUS_DOWN If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+android:nextFocusForward : Defines the next view to give focus to when the next focus is FOCUS_FORWARD If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+android:nextFocusLeft : Defines the next view to give focus to when the next focus is FOCUS_LEFT. [reference]
+android:nextFocusRight : Defines the next view to give focus to when the next focus is FOCUS_RIGHT If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+android:nextFocusUp : Defines the next view to give focus to when the next focus is FOCUS_UP If the reference refers to a view that does not exist or is part of a hierarchy that is invisible, a java.lang.RuntimeException will result when the reference is accessed. [reference]
+android:numColumns : Defines how many columns to show. [integer, enum]
+android:numStars : The number of stars (or rating items) to show. [integer]
+android:numeric : If set, specifies that this TextView has a numeric input method. * Deprecated: Use inputType instead. [flag]
+android:onClick : Name of the method in this View's context to invoke when the view is clicked. [string]
+android:orientation : Should the layout be a column or a row? Use "horizontal" for a row, "vertical" for a column. [enum]
+android:outAnimation : Identifier for the animation to use when a view is hidden. [reference]
+android:overScrollFooter : Drawable to draw below list content. [reference, color]
+android:overScrollHeader : Drawable to draw above list content. [reference, color]
+android:overScrollMode : Defines over-scrolling behavior. [enum]
+android:padding : Sets the padding, in pixels, of all four edges. [dimension]
+android:paddingBottom : Sets the padding, in pixels, of the bottom edge; see padding. [dimension]
+android:paddingLeft : Sets the padding, in pixels, of the left edge; see padding. [dimension]
+android:paddingRight : Sets the padding, in pixels, of the right edge; see padding. [dimension]
+android:paddingTop : Sets the padding, in pixels, of the top edge; see padding. [dimension]
+android:password : Whether the characters of the field are displayed as password dots instead of themselves. * Deprecated: Use inputType instead. [boolean]
+android:persistentDrawingCache : Defines the persistence of the drawing cache. [flag]
+android:phoneNumber : If set, specifies that this TextView has a phone number input method. * Deprecated: Use inputType instead. [boolean]
+android:popupBackground : Background drawable to use for the dropdown in spinnerMode="dropdown". [reference, color]
+android:popupPromptView : Reference to a layout to use for displaying a prompt in the dropdown for spinnerMode="dropdown". [reference]
+android:privateImeOptions : An addition content type description to supply to the input method attached to the text view, which is private to the implementation of the input method. [string]
+android:progress : Defines the default progress value, between 0 and max. [integer]
+android:progressDrawable : Drawable used for the progress mode. [reference]
+android:prompt : The prompt to display when the spinner's dialog is shown. [reference]
+android:queryHint : An optional query hint string to be displayed in the empty query field. [string]
+android:quickContactWindowSize : [enum]
+android:rating : The rating to set by default. [float]
+android:rotation : rotation of the view, in degrees. [float]
+android:rotationX : rotation of the view around the x axis, in degrees. [float]
+android:rotationY : rotation of the view around the y axis, in degrees. [float]
+android:saveEnabled : If unset, no state will be saved for this view when it is being frozen. [boolean]
+android:scaleType : Controls how the image should be resized or moved to match the size of this ImageView. [enum]
+android:scaleX : scale of the view in the x direction. [float]
+android:scaleY : scale of the view in the y direction. [float]
+android:scrollHorizontally : Whether the text is allowed to be wider than the view (and therefore can be scrolled horizontally). [boolean]
+android:scrollX : The initial horizontal scroll offset, in pixels. [dimension]
+android:scrollY : The initial vertical scroll offset, in pixels. [dimension]
+android:scrollbarAlwaysDrawHorizontalTrack : Defines whether the horizontal scrollbar track should always be drawn. [boolean]
+android:scrollbarAlwaysDrawVerticalTrack : Defines whether the vertical scrollbar track should always be drawn. [boolean]
+android:scrollbarDefaultDelayBeforeFade : Defines the delay in milliseconds that a scrollbar waits before fade out. [integer]
+android:scrollbarFadeDuration : Defines the delay in milliseconds that a scrollbar takes to fade out. [integer]
+android:scrollbarSize : Sets the width of vertical scrollbars and height of horizontal scrollbars. [dimension]
+android:scrollbarStyle : Controls the scrollbar style and position. [enum]
+android:scrollbarThumbHorizontal : Defines the horizontal scrollbar thumb drawable. [reference]
+android:scrollbarThumbVertical : Defines the vertical scrollbar thumb drawable. [reference]
+android:scrollbarTrackHorizontal : Defines the horizontal scrollbar track drawable. [reference]
+android:scrollbarTrackVertical : Defines the vertical scrollbar track drawable. [reference]
+android:scrollbars : Defines which scrollbars should be displayed on scrolling or not. [flag]
+android:scrollingCache : When set to true, the list uses a drawing cache during scrolling. [boolean]
+android:secondaryProgress : Defines the secondary progress value, between 0 and max. [integer]
+android:selectAllOnFocus : If the text is selectable, select it all when the view takes focus instead of moving the cursor to the start or end. [boolean]
+android:selectedDateVerticalBar : Drawable for the vertical bar shown at the beggining and at the end of a selected date. [reference]
+android:selectedWeekBackgroundColor : The background color for the selected week. [reference, color]
+android:selectionDivider : @hide The divider for making the selection area. [reference]
+android:selectionDividerHeight : @hide The height of the selection divider. [dimension]
+android:shadowColor : Place a shadow of the specified color behind the text. [color]
+android:shadowDx : Horizontal offset of the shadow. [float]
+android:shadowDy : Vertical offset of the shadow. [float]
+android:shadowRadius : Radius of the shadow. [float]
+android:showDividers : Setting for which dividers to show. [flag]
+android:showWeekNumber : Whether do show week numbers. [boolean]
+android:shownWeekCount : The number of weeks to be shown. [integer]
+android:shrinkColumns : The zero-based index of the columns to shrink. [string]
+android:singleLine : Constrains the text to a single horizontally scrolling line instead of letting it wrap onto multiple lines, and advances focus instead of inserting a newline when you press the enter key. * Deprecated: This attribute is deprecated and is replaced by the textMultiLine flag in the inputType attribute. Use caution when altering existing layouts, as the default value of singeLine is false (multi-line mode), but if you specify any value for inputType, the default is single-line mode. (If both singleLine and inputType attributes are found, the inputType flags will override the value of singleLine.). [boolean]
+android:smoothScrollbar : When set to true, the list will use a more refined calculation method based on the pixels height of the items visible on screen. [boolean]
+android:solidColor : @hide Color for the solid color background if such for optimized rendering. [reference, color]
+android:soundEffectsEnabled : Boolean that controls whether a view should have sound effects enabled for events such as clicking and touching. [boolean]
+android:spacing : [dimension]
+android:spinnerMode : Display mode for spinner options. [enum]
+android:spinnersShown : Whether the spinners are shown. [boolean]
+android:splitMotionEvents : Sets whether this ViewGroup should split MotionEvents to separate child views during touch event dispatch. [boolean]
+android:src : Sets a drawable as the content of this ImageView. [reference, color]
+android:stackFromBottom : Used by ListView and GridView to stack their content from the bottom. [boolean]
+android:startYear : The first year (inclusive), for example "1940". [integer]
+android:stepSize : The step size of the rating. [float]
+android:stretchColumns : The zero-based index of the columns to stretch. [string]
+android:stretchMode : Defines how columns should stretch to fill the available empty space, if any. [enum]
+style : A reference to a custom style [reference]
+android:tabLayout : Layout used to organize each tab's content. [reference]
+android:tabStripEnabled : Determines whether the strip under the tab indicators is drawn or not. [boolean]
+android:tabStripLeft : Drawable used to draw the left part of the strip underneath the tabs. [reference]
+android:tabStripRight : Drawable used to draw the right part of the strip underneath the tabs. [reference]
+android:tag : Supply a tag for this view containing a String, to be retrieved later with View.getTag() or searched for with View.findViewWithTag() . [string]
+android:text : Text to display. [string]
+android:textAppearance : Base text color, typeface, size, and style. [reference]
+android:textColor : Text color. [reference, color]
+android:textColorHighlight : Color of the text selection highlight. [reference, color]
+android:textColorHint : Color of the hint text. [reference, color]
+android:textColorLink : Text color for links. [reference, color]
+android:textCursorDrawable : Reference to a drawable that will be drawn under the insertion cursor. [reference]
+android:textEditNoPasteWindowLayout : Variation of textEditPasteWindowLayout displayed when the clipboard is empty. [reference]
+android:textEditPasteWindowLayout : The layout of the view that is displayed on top of the cursor to paste inside a TextEdit field. [reference]
+android:textEditSideNoPasteWindowLayout : Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. [reference]
+android:textEditSidePasteWindowLayout : Used instead of textEditPasteWindowLayout when the window is moved on the side of the insertion cursor because it would be clipped if it were positioned on top. [reference]
+android:textFilterEnabled : When set to true, the list will filter results as the user types. [boolean]
+android:textIsSelectable : Indicates that the content of a non-editable text can be selected. [boolean]
+android:textOff : The text for the button when it is not checked. [string]
+android:textOn : The text for the button when it is checked. [string]
+android:textScaleX : Sets the horizontal scaling factor for the text. [float]
+android:textSelectHandle : Reference to a drawable that will be used to display a text selection anchor for positioning the cursor within text. [reference]
+android:textSelectHandleLeft : Reference to a drawable that will be used to display a text selection anchor on the left side of a selection region. [reference]
+android:textSelectHandleRight : Reference to a drawable that will be used to display a text selection anchor on the right side of a selection region. [reference]
+android:textSize : Size of the text. [dimension]
+android:textStyle : Style (bold, italic, bolditalic) for the text. [flag]
+android:thumb : Draws the thumb on a seekbar. [reference]
+android:thumbOffset : An offset for the thumb that allows it to extend out of the range of the track. [dimension]
+android:tint : Set a tinting color for the image. [color]
+android:topOffset : Extra offset for the handle at the top of the SlidingDrawer. [dimension]
+android:transcriptMode : Sets the transcript mode for the list. [enum]
+android:transformPivotX : x location of the pivot point around which the view will rotate and scale. [dimension]
+android:transformPivotY : y location of the pivot point around which the view will rotate and scale. [dimension]
+android:translationX : translation in x of the view. [dimension]
+android:translationY : translation in y of the view. [dimension]
+android:typeface : Typeface (normal, sans, serif, monospace) for the text. [enum]
+android:uncertainGestureColor : Color used to draw the user's strokes until we are sure it's a gesture. [color]
+android:unfocusedMonthDateColor : The color for the dates of an unfocused month. [reference, color]
+android:unselectedAlpha : Sets the alpha on the items that are not selected. [float]
+android:verticalScrollbarPosition : Determines which side the vertical scroll bar should be placed on. [enum]
+android:verticalSpacing : Defines the default vertical spacing between rows. [dimension]
+android:visibility : Controls the initial visibility of the view. [enum]
+android:weekDayTextAppearance : The text appearance for the week day abbreviation of the calendar header. [reference]
+android:weekNumberColor : The color for the week numbers. [reference, color]
+android:weekSeparatorLineColor : The color for the sepatator line between weeks. [reference, color]
+android:weightSum : Defines the maximum weight sum. [float]
+android:width : Makes the TextView be exactly this many pixels wide. [dimension]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion33.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion33.txt
new file mode 100644
index 0000000..f9c008d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion33.txt
@@ -0,0 +1,3 @@
+Code completion in completionvalues1.xml for <item name="android:allowSingleTap">^</item>:
+true
+false
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion34.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion34.txt
new file mode 100644
index 0000000..861a413
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion34.txt
@@ -0,0 +1,3 @@
+Code completion in completionvalues1.xml for <item name="android:alwaysDrawnWithCache">^ false </item>:
+true
+false
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion35.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion35.txt
new file mode 100644
index 0000000..25e8a0b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion35.txt
@@ -0,0 +1,3 @@
+Code completion in completionvalues1.xml for <item name="android:alwaysDrawnWithCache"> ^false </item>:
+true
+false
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion36.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion36.txt
new file mode 100644
index 0000000..d85133f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion36.txt
@@ -0,0 +1,2 @@
+Code completion in completionvalues1.xml for <item name="android:alwaysDrawnWithCache"> f^alse </item>:
+false
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion37.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion37.txt
new file mode 100644
index 0000000..1d41351
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion37.txt
@@ -0,0 +1,2 @@
+Code completion in completionvalues1.xml for <item name="android:orientation">h^</item>:
+horizontal
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion38.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion38.txt
new file mode 100644
index 0000000..8819bcd
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1-expected-completion38.txt
@@ -0,0 +1,6 @@
+Code completion in completionvalues1.xml for c^:
+center_vertical
+center_horizontal
+center
+clip_vertical
+clip_horizontal
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1.xml
new file mode 100644
index 0000000..89b5f46
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/completionvalues1.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="stylename">
+ <item name="android:textSize">17sp</item>
+ <item name="android:textColor">@color/title_color</item>
+ <item name="android:shadowColor">@an</item>
+ <item name="android:gravity"> </item>
+ <item name="gr">@color/title_color</item>
+ <item name="an">@color/title_color</item>
+ <item ></item>
+ <item name=""></item>
+ <item name="android:allowSingleTap"></item>
+ <item name="android:alwaysDrawnWithCache"> false </item>
+ <item name="android:orientation">h</item>
+ <item name="android:gravity">
+ c
+ </item>
+ </style>
+</resources>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion47.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion47.txt
new file mode 100644
index 0000000..edf4892
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion47.txt
@@ -0,0 +1,13 @@
+Code completion in drawable1.xml for ^<layer-list:
+<animated-rotate />
+<animation-list /> : Drawable used to render several animated frames.
+<bitmap /> : Drawable used to draw bitmaps.
+<clip />
+<color /> : Drawable used to draw a single color.
+<inset />
+<layer-list ></layer-list> : Drawable used to render several drawables stacked on top of each other.
+<nine-patch /> : Drawable used to draw 9-patches.
+<rotate /> : Drawable used to rotate another drawable.
+<scale />
+<selector ></selector> : Drawable used to render several states.
+<shape ></shape> : Drawable used to render a geometric shape, with a gradient or a solid color.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion48.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion48.txt
new file mode 100644
index 0000000..f98bb4c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion48.txt
@@ -0,0 +1,3 @@
+Code completion in drawable1.xml for ^xmlns:android:
+android:opacity : Indicates the opacity of the layer. [enum]
+xmlns:android
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion49.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion49.txt
new file mode 100644
index 0000000..4d3dfe3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion49.txt
@@ -0,0 +1,7 @@
+Code completion in drawable1.xml for <item ^></item>:
+android:left : Left coordinate of the layer. [dimension]
+android:top : Top coordinate of the layer. [dimension]
+android:right : Right coordinate of the layer. [dimension]
+android:bottom : Bottom coordinate of the layer. [dimension]
+android:drawable : Drawable used to render the layer. [reference]
+android:id : Identifier of the layer. [reference]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion50.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion50.txt
new file mode 100644
index 0000000..90eab10
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1-expected-completion50.txt
@@ -0,0 +1,13 @@
+Code completion in drawable1.xml for <item >^</item>:
+<animated-rotate />
+<animation-list /> : Drawable used to render several animated frames.
+<bitmap /> : Drawable used to draw bitmaps.
+<clip />
+<color /> : Drawable used to draw a single color.
+<inset />
+<layer-list ></layer-list> : Drawable used to render several drawables stacked on top of each other.
+<nine-patch /> : Drawable used to draw 9-patches.
+<rotate /> : Drawable used to rotate another drawable.
+<scale />
+<selector ></selector> : Drawable used to render several states.
+<shape ></shape> : Drawable used to render a geometric shape, with a gradient or a solid color.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1.xml
new file mode 100644
index 0000000..9513f17
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable1.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item ></item>
+</layer-list>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2-expected-completion51.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2-expected-completion51.txt
new file mode 100644
index 0000000..1471845
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2-expected-completion51.txt
@@ -0,0 +1,10 @@
+Code completion in drawable2.xml for ^android:innerRadiusRatio="2":
+android:visible : Indicates whether the drawable should intially be visible. [boolean]
+android:dither : Enables or disables dithering. [boolean]
+android:shape : Indicates what shape to fill with a gradient. [enum]
+android:innerRadiusRatio : Inner radius of the ring expressed as a ratio of the ring's width. [float]
+android:thicknessRatio : Thickness of the ring expressed as a ratio of the ring's width. [float]
+android:innerRadius : Inner radius of the ring. [dimension]
+android:thickness : Thickness of the ring. [dimension]
+android:useLevel : Indicates whether the drawable's level affects the way the gradient is drawn.
+xmlns:android
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2-expected-completion52.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2-expected-completion52.txt
new file mode 100644
index 0000000..2a28533
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2-expected-completion52.txt
@@ -0,0 +1,7 @@
+Code completion in drawable2.xml for ^<gradient:
+<corners /> : Describes the corners for the rectangle shape of a GradientDrawable.
+<gradient /> : Used to describe the gradient used to fill the shape of a GradientDrawable.
+<padding /> : Used to specify the optional padding of a GradientDrawable.
+<size /> : Used to specify the size of the shape for GradientDrawable.
+<solid /> : Used to fill the shape of GradientDrawable with a solid color.
+<stroke /> : Used to describe the optional stroke of a GradientDrawable.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2.xml
new file mode 100644
index 0000000..c6a672f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/drawable2.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:innerRadiusRatio="2">
+ <gradient />
+</shape>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1b.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1b.diff
new file mode 100644
index 0000000..d734ccf
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1b.diff
@@ -0,0 +1,3 @@
+< <Button android:text="Button"
+---
+> <Button style="@style/newstyle" android:text="Button"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1c.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1c.diff
new file mode 100644
index 0000000..2ebc91d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1c.diff
@@ -0,0 +1,4 @@
+ android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+ </FrameLayout>
+---
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1d.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1d.diff
new file mode 100644
index 0000000..ec560b3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract1d.diff
@@ -0,0 +1,6 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="fill_parent"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract2.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract2.diff
new file mode 100644
index 0000000..ec560b3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract2.diff
@@ -0,0 +1,6 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="fill_parent"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract3.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract3.diff
new file mode 100644
index 0000000..f7fd22b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract3.diff
@@ -0,0 +1,15 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="wrap_content"
+< android:textColor="#FF0000" android:textSize="20pt"
+< android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+< android:id="@+id/button2" android:layout_alignParentBottom="true"></Button>
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="wrap_content"
+> android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="fill_parent"
+> android:textColor="#FF00FF" android:id="@+id/button2" android:layout_alignParentBottom="true"></Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract4.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract4.diff
new file mode 100644
index 0000000..a8e2af4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract4.diff
@@ -0,0 +1,7 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="fill_parent"
+> android:textSize="20pt"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract5.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract5.diff
new file mode 100644
index 0000000..bcaff2a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract5.diff
@@ -0,0 +1,8 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="wrap_content"
+< android:textColor="#FF0000" android:textSize="20pt"
+< android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+---
+> <Button style="@style/newstyle" android:text="Button"
+> android:layout_width="wrap_content" android:layout_height="wrap_content"
+> android:textColor="#FF0000" android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract6.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract6.diff
new file mode 100644
index 0000000..1db5e38
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract6.diff
@@ -0,0 +1,6 @@
+< <Button android:text="Button"
+< android:layout_width="wrap_content" android:layout_height="wrap_content"
+< android:textColor="#FF0000" android:textSize="20pt"
+< android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+---
+> <Button style="@style/newstyle" android:id="@+id/button1" ></Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract8.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract8.diff
new file mode 100644
index 0000000..2ebc91d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1-expected-extract8.diff
@@ -0,0 +1,4 @@
+ android:layout_width="wrap_content" android:layout_height="fill_parent"
+< android:textColor="#FF00FF" android:textSize="20pt"
+ </FrameLayout>
+---
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.info
new file mode 100644
index 0000000..69f7739
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.info
@@ -0,0 +1,3 @@
+android.widget.LinearLayout [0,36,140,320] <LinearLayout>
+ android.widget.Button [0,0,140,62] <Button>
+ android.widget.Button [0,62,140,284] <Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.xml
new file mode 100644
index 0000000..64c49b2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle1.xml
@@ -0,0 +1,11 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content" android:layout_height="match_parent">
+ <Button android:text="Button"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:textColor="#FF0000" android:textSize="20pt"
+ android:id="@+id/button1" android:layout_alignParentBottom="true"></Button>
+ <Button android:text="Button"
+ android:layout_width="wrap_content" android:layout_height="fill_parent"
+ android:textColor="#FF00FF" android:textSize="20pt"
+ android:id="@+id/button2" android:layout_alignParentBottom="true"></Button>
+</FrameLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2-expected-extract7.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2-expected-extract7.diff
new file mode 100644
index 0000000..84c2ad7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2-expected-extract7.diff
@@ -0,0 +1,13 @@
+< <Button foo:text="Button"
+< foo:layout_width="wrap_content" foo:layout_height="wrap_content"
+< foo:textColor="#FF0000" foo:textSize="20pt"
+< foo:id="@+id/button1" foo:layout_alignParentBottom="true"></Button>
+< <Button foo:text="Button"
+< foo:layout_width="wrap_content" foo:layout_height="fill_parent"
+< foo:textColor="#00FF00" foo:textSize="20pt"
+---
+> <Button style="@style/newstyle" foo:text="Button"
+> foo:layout_width="wrap_content" foo:layout_height="wrap_content"
+> foo:textColor="#FF0000" foo:id="@+id/button1" foo:layout_alignParentBottom="true"></Button>
+> <Button style="@style/newstyle" foo:text="Button"
+> foo:layout_width="wrap_content" foo:layout_height="fill_parent"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.info
new file mode 100644
index 0000000..69f7739
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.info
@@ -0,0 +1,3 @@
+android.widget.LinearLayout [0,36,140,320] <LinearLayout>
+ android.widget.Button [0,0,140,62] <Button>
+ android.widget.Button [0,62,140,284] <Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.xml
new file mode 100644
index 0000000..3cb966f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/extractstyle2.xml
@@ -0,0 +1,11 @@
+<LinearLayout xmlns:foo="http://schemas.android.com/apk/res/android"
+ foo:layout_width="wrap_content" foo:layout_height="match_parent" foo:orientation="vertical">
+ <Button foo:text="Button"
+ foo:layout_width="wrap_content" foo:layout_height="wrap_content"
+ foo:textColor="#FF0000" foo:textSize="20pt"
+ foo:id="@+id/button1" foo:layout_alignParentBottom="true"></Button>
+ <Button foo:text="Button"
+ foo:layout_width="wrap_content" foo:layout_height="fill_parent"
+ foo:textColor="#00FF00" foo:textSize="20pt"
+ foo:id="@+id/button2" foo:layout_alignParentBottom="true"></Button>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion14.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion14.txt
new file mode 100644
index 0000000..478e435
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion14.txt
@@ -0,0 +1,10 @@
+Code completion in manifest.xml for android.permission.ACC^ESS_NETWORK_STATE:
+android.permission.ACCESS_CHECKIN_PROPERTIES
+android.permission.ACCESS_COARSE_LOCATION
+android.permission.ACCESS_FINE_LOCATION
+android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
+android.permission.ACCESS_MOCK_LOCATION
+android.permission.ACCESS_NETWORK_STATE
+android.permission.ACCESS_SURFACE_FLINGER
+android.permission.ACCESS_WIFI_STATE
+android.permission.ACCOUNT_MANAGER
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion15.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion15.txt
new file mode 100644
index 0000000..c6b3538
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion15.txt
@@ -0,0 +1,3 @@
+Code completion in manifest.xml for android.intent.category.L^AUNCHER:
+android.intent.category.LAUNCHER
+android.intent.category.LE_DESK_DOCK
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion16.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion16.txt
new file mode 100644
index 0000000..4795c5c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion16.txt
@@ -0,0 +1,14 @@
+Code completion in manifest.xml for <^application android:i:
+application : The "application" tag describes application-level components contained in the package, as well as general application attributes.
+compatible-screens
+instrumentation : Attributes that can be supplied in an AndroidManifest.xml "instrumentation" tag, a child of the root manifest tag.
+original-package : Private tag to declare the original package name that this package is based on.
+permission : The "permission" tag declares a security permission that can be used to control access from other packages to specific components or features in your package (or other packages).
+permission-group : The "permission-group" tag declares a logical grouping of related permissions.
+permission-tree : The "permission-tree" tag declares the base of a tree of permission values: it declares that this package has ownership of the given permission name, as well as all names underneath it (separated by '.').
+protected-broadcast : Private tag to declare system protected broadcast actions.
+supports-screens : The "supports-screens" specifies the screen dimensions an application supports.
+uses-configuration : The "uses-configuration" tag specifies a specific hardware configuration value used by the application.
+uses-feature : The "uses-feature" tag specifies a specific feature used by the application.
+uses-permission : The "uses-permission" tag requests a "permission" that the containing package must be granted in order for it to operate correctly.
+uses-sdk : The "uses-sdk" tag describes the SDK features that the containing package must be running on to operate correctly.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion17.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion17.txt
new file mode 100644
index 0000000..6d966da
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion17.txt
@@ -0,0 +1,7 @@
+Code completion in manifest.xml for ^android:versionCode="1":
+package : This attribute gives a unique name for the package, using a Java-style naming convention to avoid name collisions. For example, applications published by Google could have names of the form com.google.app.appname
+android:versionCode : Internal version code. [integer]
+android:versionName : The text shown to the user to indicate the version they have. [string]
+android:sharedUserId : Specify the name of a user ID that will be shared between multiple packages. [string]
+android:sharedUserLabel : Specify a label for the shared user UID of this package. [reference]
+android:installLocation : The default install location defined by an application. [enum]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion18.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion18.txt
new file mode 100644
index 0000000..6fd1096
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-completion18.txt
@@ -0,0 +1,27 @@
+Code completion in manifest.xml for <activity android:^name=".TestActivity":
+android:name : Required name of the class implementing the activity, deriving from android.app.Activity. [string]
+android:theme : The overall theme to use for an activity. [reference]
+android:label : A user-legible name for the given item. [string, reference]
+android:description : Descriptive text for the associated data. [reference]
+android:icon : A Drawable resource providing a graphical representation of its associated item. [reference]
+android:logo : A Drawable resource providing an extended graphical logo for its associated item. [reference]
+android:launchMode : Specify how an activity should be launched. [enum]
+android:screenOrientation : Specify the orientation an activity should be run in. [enum]
+android:configChanges : Specify one or more configuration changes that the activity will handle itself. [flag]
+android:permission : Specify a permission that a client is required to have in order to use the associated object. [string]
+android:multiprocess : Specify whether a component is allowed to have multiple instances of itself running in different processes. [boolean]
+android:process : Specify a specific process that the associated code is to run in. [string]
+android:taskAffinity : Specify a task name that activities have an "affinity" to. [string]
+android:allowTaskReparenting : Specify that an activity can be moved out of a task it is in to the task it has an affinity for when appropriate. [boolean]
+android:finishOnTaskLaunch : Specify whether an activity should be finished when its task is brought to the foreground by relaunching from the home screen. [boolean]
+android:finishOnCloseSystemDialogs : Specify whether an activity should be finished when a "close system windows" request has been made. [boolean]
+android:clearTaskOnLaunch : Specify whether an activity's task should be cleared when it is re-launched from the home screen. [boolean]
+android:noHistory : Specify whether an activity should be kept in its history stack. [boolean]
+android:alwaysRetainTaskState : Specify whether an acitivty's task state should always be maintained by the system, or if it is allowed to reset the task to its initial state in certain situations. [boolean]
+android:stateNotNeeded : Indicates that an Activity does not need to have its freeze state (as returned by onSaveInstanceState retained in order to be restarted. [boolean]
+android:excludeFromRecents : Indicates that an Activity should be excluded from the list of recently launched activities. [boolean]
+android:enabled : Specify whether the activity is enabled or not (that is, can be instantiated by the system). [boolean]
+android:exported : Flag indicating whether the given application component is available to other applications. [boolean]
+android:windowSoftInputMode : Specify the default soft-input mode for the main window of this activity. [flag]
+android:immersive : Flag declaring this activity to be 'immersive'; immersive activities should not be interrupted with other activities or notifications. [boolean]
+android:hardwareAccelerated : <p>Flag indicating whether the application's rendering should be hardware accelerated if possible. [boolean]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate10.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate10.txt
new file mode 100644
index 0000000..c745f54
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate10.txt
@@ -0,0 +1,6 @@
+Go To Declaration in manifest.xml for <uses-permission android:name="android.permission.AC^CESS_NETWORK_STATE" />:
+Open XML Declaration : [android.permission.ACCESS_NETWORK_STATE]
+
+
+After open, a browser is shown with this URL:
+ reference/android/Manifest.permission.html#ACCESS_NETWORK_STATE
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate11a.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate11a.txt
new file mode 100644
index 0000000..eaeddf9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate11a.txt
@@ -0,0 +1,6 @@
+Go To Declaration in manifest.xml for <action android:name="android.intent.ac^tion.MAIN" />:
+Open XML Declaration : [android.intent.action.MAIN]
+
+
+After open, a browser is shown with this URL:
+ reference/android/content/Intent.html#ACTION_MAIN
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate11g.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate11g.txt
new file mode 100644
index 0000000..9acb330
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate11g.txt
@@ -0,0 +1,6 @@
+Go To Declaration in manifest.xml for <category android:name="android.intent.category.LA^UNCHER" />:
+Open XML Declaration : [android.intent.category.LAUNCHER]
+
+
+After open, a browser is shown with this URL:
+ reference/android/content/Intent.html#CATEGORY_LAUNCHER
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate9a.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate9a.txt
new file mode 100644
index 0000000..2221e37
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest-expected-navigate9a.txt
@@ -0,0 +1,6 @@
+Go To Declaration in manifest.xml for <activity android:name=".Test^Activity":
+Open XML Declaration : [.TestActivity]
+
+
+After open, the selected text is:
+^<?xml version="1.0" encoding="utf-8"?>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest.xml
new file mode 100644
index 0000000..2c0024f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/manifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="foo.bar"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="11" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+
+ <application android:icon="@drawable/icon" android:label="@string/app_name">
+ <activity android:name=".TestActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+</manifest>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/metadata.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/metadata.xml
new file mode 100644
index 0000000..9262f97
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/metadata.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout android:id="@+id/LinearLayout1"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ListView android:layout_width="match_parent" android:id="@+id/listView1"
+ android:layout_height="wrap_content">
+ </ListView>
+ <Button android:text="Button" android:id="@+id/button1"/>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate1.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate1.txt
new file mode 100644
index 0000000..7308a48
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate1.txt
@@ -0,0 +1,7 @@
+Go To Declaration in navigation1.xml for android:text="@string/app^_name":
+Open Declaration in values/strings.xml : [@string/app_name]
+ L/PROJECTNAME/res/values/strings.xml
+
+
+After open, the selected text is:
+ [^<string name="app_name">PROJECTNAME</string>]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate12.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate12.txt
new file mode 100644
index 0000000..23fa07e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate12.txt
@@ -0,0 +1,6 @@
+Go To Declaration in navigation1.xml for <my.Cust^omView></my.CustomView>:
+Open XML Declaration : [my.CustomView]
+
+
+After open, the selected text is:
+^<?xml version="1.0" encoding="utf-8"?>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate2.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate2.txt
new file mode 100644
index 0000000..f91e1f9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate2.txt
@@ -0,0 +1,7 @@
+Go To Declaration in navigation1.xml for marginLeft="@android:dimen/app_ico^n_size":
+Open Declaration in values/dimens.xml : [@android:dimen/app_icon_size]
+ data/res/values/dimens.xml
+
+
+After open, the selected text is:
+ <dimen name="app_icon_size">^48dip</dimen>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate3.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate3.txt
new file mode 100644
index 0000000..53776a0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate3.txt
@@ -0,0 +1,7 @@
+Go To Declaration in navigation1.xml for style="@android:style/Widget.B^utton":
+Open Declaration in values/styles.xml : [@android:style/Widget.Button]
+ data/res/values/styles.xml
+
+
+After open, the selected text is:
+ <style name="Widget.Button">^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate4.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate4.txt
new file mode 100644
index 0000000..ec5ee38
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate4.txt
@@ -0,0 +1,89 @@
+Go To Declaration in navigation1.xml for android:text="@android:st^ring/ok":
+Open Declaration in values-en-rGB/strings.xml : [@android:string/ok]
+ data/res/values-en-rGB/strings.xml
+Open Declaration in values/strings.xml : [@android:string/ok]
+ data/res/values/strings.xml
+Open Declaration in values-ar/strings.xml : [@android:string/ok]
+ data/res/values-ar/strings.xml
+Open Declaration in values-bg/strings.xml : [@android:string/ok]
+ data/res/values-bg/strings.xml
+Open Declaration in values-ca/strings.xml : [@android:string/ok]
+ data/res/values-ca/strings.xml
+Open Declaration in values-cs/strings.xml : [@android:string/ok]
+ data/res/values-cs/strings.xml
+Open Declaration in values-da/strings.xml : [@android:string/ok]
+ data/res/values-da/strings.xml
+Open Declaration in values-de/strings.xml : [@android:string/ok]
+ data/res/values-de/strings.xml
+Open Declaration in values-el/strings.xml : [@android:string/ok]
+ data/res/values-el/strings.xml
+Open Declaration in values-es/strings.xml : [@android:string/ok]
+ data/res/values-es/strings.xml
+Open Declaration in values-es-rUS/strings.xml : [@android:string/ok]
+ data/res/values-es-rUS/strings.xml
+Open Declaration in values-fa/strings.xml : [@android:string/ok]
+ data/res/values-fa/strings.xml
+Open Declaration in values-fi/strings.xml : [@android:string/ok]
+ data/res/values-fi/strings.xml
+Open Declaration in values-fr/strings.xml : [@android:string/ok]
+ data/res/values-fr/strings.xml
+Open Declaration in values-hr/strings.xml : [@android:string/ok]
+ data/res/values-hr/strings.xml
+Open Declaration in values-hu/strings.xml : [@android:string/ok]
+ data/res/values-hu/strings.xml
+Open Declaration in values-in/strings.xml : [@android:string/ok]
+ data/res/values-in/strings.xml
+Open Declaration in values-it/strings.xml : [@android:string/ok]
+ data/res/values-it/strings.xml
+Open Declaration in values-iw/strings.xml : [@android:string/ok]
+ data/res/values-iw/strings.xml
+Open Declaration in values-ja/strings.xml : [@android:string/ok]
+ data/res/values-ja/strings.xml
+Open Declaration in values-ko/strings.xml : [@android:string/ok]
+ data/res/values-ko/strings.xml
+Open Declaration in values-lt/strings.xml : [@android:string/ok]
+ data/res/values-lt/strings.xml
+Open Declaration in values-lv/strings.xml : [@android:string/ok]
+ data/res/values-lv/strings.xml
+Open Declaration in values-nb/strings.xml : [@android:string/ok]
+ data/res/values-nb/strings.xml
+Open Declaration in values-nl/strings.xml : [@android:string/ok]
+ data/res/values-nl/strings.xml
+Open Declaration in values-pl/strings.xml : [@android:string/ok]
+ data/res/values-pl/strings.xml
+Open Declaration in values-pt/strings.xml : [@android:string/ok]
+ data/res/values-pt/strings.xml
+Open Declaration in values-pt-rPT/strings.xml : [@android:string/ok]
+ data/res/values-pt-rPT/strings.xml
+Open Declaration in values-rm/strings.xml : [@android:string/ok]
+ data/res/values-rm/strings.xml
+Open Declaration in values-ro/strings.xml : [@android:string/ok]
+ data/res/values-ro/strings.xml
+Open Declaration in values-ru/strings.xml : [@android:string/ok]
+ data/res/values-ru/strings.xml
+Open Declaration in values-sk/strings.xml : [@android:string/ok]
+ data/res/values-sk/strings.xml
+Open Declaration in values-sl/strings.xml : [@android:string/ok]
+ data/res/values-sl/strings.xml
+Open Declaration in values-sr/strings.xml : [@android:string/ok]
+ data/res/values-sr/strings.xml
+Open Declaration in values-sv/strings.xml : [@android:string/ok]
+ data/res/values-sv/strings.xml
+Open Declaration in values-th/strings.xml : [@android:string/ok]
+ data/res/values-th/strings.xml
+Open Declaration in values-tl/strings.xml : [@android:string/ok]
+ data/res/values-tl/strings.xml
+Open Declaration in values-tr/strings.xml : [@android:string/ok]
+ data/res/values-tr/strings.xml
+Open Declaration in values-uk/strings.xml : [@android:string/ok]
+ data/res/values-uk/strings.xml
+Open Declaration in values-vi/strings.xml : [@android:string/ok]
+ data/res/values-vi/strings.xml
+Open Declaration in values-zh-rCN/strings.xml : [@android:string/ok]
+ data/res/values-zh-rCN/strings.xml
+Open Declaration in values-zh-rTW/strings.xml : [@android:string/ok]
+ data/res/values-zh-rTW/strings.xml
+
+
+After open, the selected text is:
+ <string name="ok" msgid="5970060430562524910">^"OK"</string>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml
new file mode 100644
index 0000000..9c175fc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <Button
+ android:text="@string/app_name"
+ android:layout_marginLeft="@android:dimen/app_icon_size"
+ style="@android:style/Widget.Button"
+ android:id="@+id/button1"
+ ></Button>
+ <my.CustomView></my.CustomView>
+ <EditText
+ android:text="@android:string/ok"
+ </EditText>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-extract2.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-extract2.diff
new file mode 100644
index 0000000..141180b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-extract2.diff
@@ -0,0 +1,6 @@
+---
+ </style>
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+ </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate5.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate5.txt
new file mode 100644
index 0000000..15c91d1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate5.txt
@@ -0,0 +1,7 @@
+Go To Declaration in navigationstyles.xml for parent="android:Theme.Li^ght">:
+Open Declaration in values/themes.xml : [android:Theme.Light]
+ data/res/values/themes.xml
+
+
+After open, the selected text is:
+ <style name="Theme.Light">^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate6.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate6.txt
new file mode 100644
index 0000000..5a4f40a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate6.txt
@@ -0,0 +1,7 @@
+Go To Declaration in navigationstyles.xml for parent="android:The^me.Light">:
+Open Declaration in values/themes.xml : [android:Theme]
+ data/res/values/themes.xml
+
+
+After open, the selected text is:
+ <style name="Theme">^
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate7.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate7.txt
new file mode 100644
index 0000000..8f7eb46
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate7.txt
@@ -0,0 +1,7 @@
+Go To Declaration in navigationstyles.xml for popupBackground">@android:drawable/spinner_dr^opdown_background</item>:
+Open Declaration in drawable/spinner_dropdown_background.xml : [@android:drawable/spinner_dropdown_background]
+ data/res/drawable/spinner_dropdown_background.xml
+
+
+After open, the selected text is:
+^<?xml version="1.0" encoding="utf-8"?>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate8.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate8.txt
new file mode 100644
index 0000000..b74c676
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles-expected-navigate8.txt
@@ -0,0 +1,7 @@
+Go To Declaration in navigationstyles.xml for colorBackground"> @color/cust^om_theme_color </item>:
+Open Declaration in values/navigationstyles.xml : [@color/custom_theme_color]
+ L/PROJECTNAME/res/values/navigationstyles.xml
+
+
+After open, the selected text is:
+ [^<color name="custom_theme_color">#b0b0ff</color>]
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles.xml
new file mode 100644
index 0000000..da4bbf2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigationstyles.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="custom_theme_color">#b0b0ff</color>
+ <style name="CustomTheme" parent="android:Theme.Light">
+ <item name="android:windowBackground">@color/custom_theme_color</item>
+ <item name="android:colorBackground"> @color/custom_theme_color </item>
+ </style>
+
+ <style name="BrowserTheme" parent="@android:Theme.Black">
+ <item name="android:autoCompleteTextViewStyle">@style/AutoCompleteTextView</item>
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
+ <style name="AutoCompleteTextView">
+ <item name="android:focusable">true</item>
+ <item name="android:focusableInTouchMode">true</item>
+ <item name="android:clickable">true</item>
+ <item name="android:completionHintView">@android:layout/simple_dropdown_item_1line</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceLargeInverse</item>
+ <item name="android:completionThreshold">2</item>
+ <item name="android:dropDownSelector">@android:drawable/list_selector_background</item>
+ <item name="android:popupBackground">@android:drawable/spinner_dropdown_background</item>
+ </style>
+
+ <style name="CustomTitle" parent="@android:Theme">
+ <item name="android:windowTitleSize">60dip</item>
+ </style>
+</resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout1-expected-extract1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout1-expected-extract1.xml
new file mode 100644
index 0000000..2a0e947
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout1-expected-extract1.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Button xmlns:android="http://schemas.android.com/apk/res/android" android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout2-expected-extract2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout2-expected-extract2.xml
new file mode 100644
index 0000000..b2d1789
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout2-expected-extract2.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <ImageView android:id="@+id/android_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button" android:focusable="false" android:clickable="false" android:layout_weight="1.0" />
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+</merge>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract3.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract3.xml
new file mode 100644
index 0000000..6ba61c8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract3.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Button xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:id="@+id/button3" android:layout_height="wrap_content" android:text="Button" ></Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract4.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract4.xml
new file mode 100644
index 0000000..ee8568e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract4.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/android_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button" android:focusable="false" android:clickable="false" />
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract5.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract5.xml
new file mode 100644
index 0000000..be57066
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout3-expected-extract5.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <ImageView android:id="@+id/android_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button" android:focusable="false" android:clickable="false" android:layout_weight="1.0" />
+</merge>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout6-expected-extract6.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout6-expected-extract6.diff
new file mode 100644
index 0000000..3a4c590
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newlayout6-expected-extract6.diff
@@ -0,0 +1,11 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:id="@+id/linearLayout4" android:layout_width="match_parent">
+> <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout5" android:layout_width="wrap_content">
+> <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout6" android:layout_width="wrap_content">
+> <!-- Comment -->
+> <Button android:text="Button" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+> </LinearLayout>
+> </LinearLayout>
+> </LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles-expected-extract1.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles-expected-extract1.diff
new file mode 100644
index 0000000..d83eb49
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles-expected-extract1.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles2-expected-extract1b.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles2-expected-extract1b.diff
new file mode 100644
index 0000000..d83eb49
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles2-expected-extract1b.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract1c.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract1c.diff
new file mode 100644
index 0000000..d83eb49
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract1c.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract8.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract8.diff
new file mode 100644
index 0000000..3b4d930
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles3-expected-extract8.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle" parent="android:Widget.Button">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract1d.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract1d.diff
new file mode 100644
index 0000000..d83eb49
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract1d.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract3.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract3.diff
new file mode 100644
index 0000000..0685d94
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles4-expected-extract3.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF0000</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles5-expected-extract4.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles5-expected-extract4.diff
new file mode 100644
index 0000000..f052485
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles5-expected-extract4.diff
@@ -0,0 +1,8 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#FF00FF</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles6-expected-extract5.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles6-expected-extract5.diff
new file mode 100644
index 0000000..ce1d4aa
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles6-expected-extract5.diff
@@ -0,0 +1,8 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles7-expected-extract6.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles7-expected-extract6.diff
new file mode 100644
index 0000000..51f0812
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles7-expected-extract6.diff
@@ -0,0 +1,13 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:layout_alignParentBottom">true</item>
+> <item name="android:layout_height">wrap_content</item>
+> <item name="android:layout_width">wrap_content</item>
+> <item name="android:text">Button</item>
+> <item name="android:textColor">#FF0000</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles8-expected-extract7.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles8-expected-extract7.diff
new file mode 100644
index 0000000..8f7ad98
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/newstyles8-expected-extract7.diff
@@ -0,0 +1,9 @@
+<
+---
+> <?xml version="1.0" encoding="utf-8"?>
+> <resources xmlns:android="http://schemas.android.com/apk/res/android">
+> <style name="newstyle">
+> <item name="android:textColor">#00FF00</item>
+> <item name="android:textSize">20pt</item>
+> </style>
+> </resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix1.xml
new file mode 100644
index 0000000..2ef716b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix1.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="hello">Hello World!</string>
+ <string name="app_name">PROJECTNAME</string>
+ <string name="firststring">[^TODO]</string>
+</resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix2.xml
new file mode 100644
index 0000000..a0d04fb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix2.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="testdimen">[^1dp]</dimen>
+</resources>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix3.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix3.xml
new file mode 100644
index 0000000..8773027
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix3.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ ^
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1.xml
new file mode 100644
index 0000000..927c8d1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button android:text="@string/firststring"
+ android:id="@+id/button1"
+ android:layout_width="@dimen/testdimen"
+ android:layout_height="wrap_content">
+ </Button>
+
+ <include layout="@layout/testlayout" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
+
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix2-expected-quickFix4.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix2-expected-quickFix4.xml
new file mode 100644
index 0000000..025fa0a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix2-expected-quickFix4.xml
@@ -0,0 +1,3 @@
+< <color android:color="#0000000"/>
+---
+> <color android:color="#0000000" xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix2.xml
new file mode 100644
index 0000000..4f2a925
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix2.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <!--
+ Random comment here
+ -->
+<color android:color="#0000000"/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant1.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant1.txt
new file mode 100644
index 0000000..457239f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant1.txt
@@ -0,0 +1,3 @@
+Quick assistant in sample1a.xml for <Button android:text="Fir^stButton":
+Extract Android String : Initiates the given refactoring operation
+Extract Style : Initiates the given refactoring operation
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant2.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant2.txt
new file mode 100644
index 0000000..95187d3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant2.txt
@@ -0,0 +1,6 @@
+Quick assistant in sample1a.xml for <Bu^tton android:text:
+Wrap in Container : Initiates the given refactoring operation
+Change Widget Type : Initiates the given refactoring operation
+Change Layout : Initiates the given refactoring operation
+Extract Style : Initiates the given refactoring operation
+Extract as Include : Initiates the given refactoring operation
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant3.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant3.txt
new file mode 100644
index 0000000..8123be4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant3.txt
@@ -0,0 +1,2 @@
+Quick assistant in sample1a.xml for <Button andr^oid:text="FirstButton":
+Extract Style : Initiates the given refactoring operation
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant4.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant4.txt
new file mode 100644
index 0000000..e0cb98b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-assistant4.txt
@@ -0,0 +1,2 @@
+Quick assistant in sample1a.xml for android:id="@+id/Linea^rLayout2":
+None found.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-changeLayout1a.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-changeLayout1a.xml
new file mode 100644
index 0000000..772f82a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-changeLayout1a.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout android:id="@+id/RelativeLayout1" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <Button android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="FirstButton" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button2" android:layout_below="@+id/button1" android:layout_marginTop="2dp" android:text="SecondButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button2"></Button>
+ <Button android:layout_toRightOf="@+id/button2" android:layout_alignBaseline="@+id/button2" android:layout_alignTop="@+id/button2" android:text="ThirdButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button3"></Button>
+ <CheckBox android:layout_toRightOf="@+id/button3" android:layout_alignBaseline="@+id/button2" android:layout_below="@+id/button1" android:id="@+id/checkBox1" android:text="CheckBox" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
+ <Button android:layout_alignParentLeft="true" android:layout_below="@+id/checkBox1" android:layout_height="wrap_content" android:text="FourthButton" android:id="@+id/button4" android:layout_width="match_parent"></Button>
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button5" android:layout_below="@+id/button4" android:layout_gravity="right" android:id="@+id/button5" android:text="FifthButton" android:layout_height="wrap_content" android:layout_width="wrap_content"></Button>
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button6" android:layout_below="@+id/button5" android:text="Button" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+</RelativeLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-changeView1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-changeView1.xml
new file mode 100644
index 0000000..f4a08d0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-changeView1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout android:id="@+id/LinearLayout2" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <RadioButton android:text="FirstButton" android:id="@+id/RadioButton1" android:layout_width="wrap_content" android:layout_height="wrap_content"></RadioButton>
+ <LinearLayout android:layout_width="match_parent" android:id="@+id/linearLayout1" android:layout_height="wrap_content">
+ <Button android:text="SecondButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button2"></Button>
+ <Button android:text="ThirdButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button3"></Button>
+ <CheckBox android:id="@+id/checkBox1" android:text="CheckBox" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
+ </LinearLayout>
+ <Button android:layout_height="wrap_content" android:text="FourthButton" android:id="@+id/button4" android:layout_width="match_parent"></Button>
+ <LinearLayout android:layout_height="wrap_content" android:id="@+id/linearLayout3" android:layout_width="match_parent">
+ <Button android:layout_gravity="right" android:id="@+id/button5" android:text="FifthButton" android:layout_height="wrap_content" android:layout_width="wrap_content"></Button>
+ </LinearLayout>
+ <LinearLayout android:layout_height="wrap_content" android:id="@+id/linearLayout4" android:layout_width="match_parent">
+ <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout5" android:layout_width="wrap_content">
+ <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout6" android:layout_width="wrap_content">
+ <RadioButton android:text="Button" android:id="@+id/RadioButton2" android:layout_width="wrap_content" android:layout_height="wrap_content"></RadioButton>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-extract6.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-extract6.diff
new file mode 100644
index 0000000..c0ebd59
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a-expected-extract6.diff
@@ -0,0 +1,9 @@
+< <LinearLayout android:layout_height="wrap_content" android:id="@+id/linearLayout4" android:layout_width="match_parent">
+< <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout5" android:layout_width="wrap_content">
+< <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout6" android:layout_width="wrap_content">
+< <Button android:text="Button" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+< </LinearLayout>
+< </LinearLayout>
+< </LinearLayout>
+---
+> <include layout="@layout/newlayout6" android:id="@+id/linearLayout4_ref" android:layout_width="match_parent" android:layout_height="wrap_content"/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a.info
new file mode 100644
index 0000000..20a23b0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a.info
@@ -0,0 +1,13 @@
+android.widget.LinearLayout [0,36,240,320] <LinearLayout>
+ android.widget.Button [0,0,70,36] <Button>
+ android.widget.LinearLayout [0,36,240,72] <LinearLayout>
+ android.widget.Button [0,2,84,38] <Button>
+ android.widget.Button [84,2,158,38] <Button>
+ android.widget.CheckBox [158,0,238,36] <CheckBox>
+ android.widget.Button [0,72,240,108] <Button>
+ android.widget.LinearLayout [0,108,240,144] <LinearLayout>
+ android.widget.Button [0,0,71,36] <Button>
+ android.widget.LinearLayout [0,144,240,180] <LinearLayout>
+ android.widget.LinearLayout [0,0,49,36] <LinearLayout>
+ android.widget.LinearLayout [0,0,49,36] <LinearLayout>
+ android.widget.Button [0,0,49,36] <Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a.xml
new file mode 100644
index 0000000..9a94935
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1a.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout android:id="@+id/LinearLayout2" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <Button android:text="FirstButton" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <LinearLayout android:layout_width="match_parent" android:id="@+id/linearLayout1" android:layout_height="wrap_content">
+ <Button android:text="SecondButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button2"></Button>
+ <Button android:text="ThirdButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button3"></Button>
+ <CheckBox android:id="@+id/checkBox1" android:text="CheckBox" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
+ </LinearLayout>
+ <Button android:layout_height="wrap_content" android:text="FourthButton" android:id="@+id/button4" android:layout_width="match_parent"></Button>
+ <LinearLayout android:layout_height="wrap_content" android:id="@+id/linearLayout3" android:layout_width="match_parent">
+ <Button android:layout_gravity="right" android:id="@+id/button5" android:text="FifthButton" android:layout_height="wrap_content" android:layout_width="wrap_content"></Button>
+ </LinearLayout>
+ <LinearLayout android:layout_height="wrap_content" android:id="@+id/linearLayout4" android:layout_width="match_parent">
+ <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout5" android:layout_width="wrap_content">
+ <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout6" android:layout_width="wrap_content">
+ <Button android:text="Button" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b-expected-changeLayout1b.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b-expected-changeLayout1b.xml
new file mode 100644
index 0000000..f47e9be
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b-expected-changeLayout1b.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ android:id="@+id/RelativeLayout1"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <Button android:layout_alignParentLeft="true" android:layout_alignParentTop="true"
+ android:text="FirstButton"
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"></Button>
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button2" android:layout_below="@+id/button1" android:layout_marginTop="2dp"
+ android:text="SecondButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button2"></Button>
+ <Button android:layout_toRightOf="@+id/button2" android:layout_alignBaseline="@+id/button2" android:layout_alignTop="@+id/button2"
+ android:text="ThirdButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button3"></Button>
+ <CheckBox android:layout_toRightOf="@+id/button3" android:layout_alignBaseline="@+id/button2" android:layout_below="@+id/button1"
+ android:id="@+id/checkBox1"
+ android:text="CheckBox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"></CheckBox>
+ <Button android:layout_alignParentLeft="true" android:layout_below="@+id/checkBox1"
+ android:layout_height="wrap_content"
+ android:text="FourthButton"
+ android:id="@+id/button4"
+ android:layout_width="match_parent"></Button>
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button5" android:layout_below="@+id/button4"
+ android:layout_gravity="right"
+ android:id="@+id/button5"
+ android:text="FifthButton"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"></Button>
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button6" android:layout_below="@+id/button5"
+ android:text="Button"
+ android:id="@+id/button6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"></Button>
+</RelativeLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b.info
new file mode 100644
index 0000000..7807227
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b.info
@@ -0,0 +1,14 @@
+android.widget.LinearLayout [0,36,240,320] <LinearLayout>
+ android.widget.Button [0,0,70,36] <Button>
+ android.widget.LinearLayout [0,36,240,72] <LinearLayout>
+ android.widget.Button [0,2,84,38] <Button>
+ android.widget.Button [84,2,158,38] <Button>
+ android.widget.CheckBox [158,0,238,36] <CheckBox>
+ android.widget.Button [0,72,240,108] <Button>
+ android.widget.LinearLayout [0,108,240,144] <LinearLayout>
+ android.widget.Button [0,0,71,36] <Button>
+ android.widget.LinearLayout [0,144,240,180] <LinearLayout>
+ android.widget.LinearLayout [0,0,49,36] <LinearLayout>
+ android.widget.LinearLayout [0,0,49,36] <LinearLayout>
+ android.widget.Button [0,0,49,36] <Button>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b.xml
new file mode 100644
index 0000000..6b800ae
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample1b.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ android:id="@+id/LinearLayout2"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <Button
+ android:text="FirstButton"
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"></Button>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:id="@+id/linearLayout1"
+ android:layout_height="wrap_content">
+ <Button
+ android:text="SecondButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button2"></Button>
+ <Button
+ android:text="ThirdButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button3"></Button>
+ <CheckBox
+ android:id="@+id/checkBox1"
+ android:text="CheckBox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"></CheckBox>
+ </LinearLayout>
+ <Button
+ android:layout_height="wrap_content"
+ android:text="FourthButton"
+ android:id="@+id/button4"
+ android:layout_width="match_parent"></Button>
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:id="@+id/linearLayout3"
+ android:layout_width="match_parent">
+ <Button
+ android:layout_gravity="right"
+ android:id="@+id/button5"
+ android:text="FifthButton"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"></Button>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:id="@+id/linearLayout4"
+ android:layout_width="match_parent">
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:id="@+id/linearLayout5"
+ android:layout_width="wrap_content">
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:id="@+id/linearLayout6"
+ android:layout_width="wrap_content">
+ <Button
+ android:text="Button"
+ android:id="@+id/button6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"></Button>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-changeLayout2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-changeLayout2.xml
new file mode 100644
index 0000000..cce3803
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-changeLayout2.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout android:id="@+id/RelativeLayout1" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <Button android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <Button android:layout_below="@+id/button1" android:layout_width="wrap_content" android:layout_alignParentLeft="true" android:id="@+id/button2" android:layout_height="wrap_content" android:text="Button"></Button>
+ <Button android:layout_width="wrap_content" android:id="@+id/button3" android:layout_below="@+id/button2" android:layout_height="wrap_content" android:text="Button" android:layout_toRightOf="@+id/button2"></Button>
+ <Button android:layout_width="wrap_content" android:id="@+id/button4" android:layout_below="@+id/button3" android:layout_height="wrap_content" android:text="Button" android:layout_toRightOf="@+id/button3"></Button>
+ <CheckBox android:layout_width="wrap_content" android:layout_below="@+id/button4" android:id="@+id/checkBox1" android:layout_height="wrap_content" android:text="CheckBox" android:layout_toLeftOf="@+id/button4"></CheckBox>
+ <Button android:layout_alignTop="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" android:id="@+id/button5" android:layout_alignParentRight="true"></Button>
+</RelativeLayout>
+
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-changeView2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-changeView2.xml
new file mode 100644
index 0000000..5a55498
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-changeView2.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <RelativeLayout android:layout_height="match_parent" android:id="@+id/relativeLayout1" android:layout_width="match_parent">
+ <Button android:layout_width="wrap_content" android:layout_alignParentLeft="true" android:id="@+id/button2" android:layout_height="wrap_content" android:text="Button"></Button>
+ <ImageButton android:layout_width="wrap_content" android:id="@+id/ImageButton1" android:layout_below="@+id/button2" android:layout_height="wrap_content" android:layout_toRightOf="@+id/button2"></ImageButton>
+ <Button android:layout_width="wrap_content" android:id="@+id/button4" android:layout_below="@+id/ImageButton1" android:layout_height="wrap_content" android:text="Button" android:layout_toRightOf="@+id/ImageButton1"></Button>
+ <CheckBox android:layout_width="wrap_content" android:layout_below="@+id/button4" android:id="@+id/checkBox1" android:layout_height="wrap_content" android:text="CheckBox" android:layout_toLeftOf="@+id/button4"></CheckBox>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/ImageButton2" android:layout_alignParentRight="true"></ImageButton>
+ </RelativeLayout>
+</LinearLayout>
+
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-extract3.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-extract3.xml
new file mode 100644
index 0000000..e4ff731
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2-expected-extract3.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <RelativeLayout android:layout_height="match_parent" android:id="@+id/relativeLayout1" android:layout_width="match_parent">
+ <Button android:layout_width="wrap_content" android:layout_alignParentLeft="true" android:id="@+id/button2" android:layout_height="wrap_content" android:text="Button"></Button>
+ <include layout="@layout/newlayout3" android:id="@+id/button3_ref" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/button2" android:layout_toRightOf="@+id/button2"/>
+ <Button android:layout_width="wrap_content" android:id="@+id/button4" android:layout_below="@+id/button3_ref" android:layout_height="wrap_content" android:text="Button" android:layout_toRightOf="@+id/button3_ref"></Button>
+ <CheckBox android:layout_width="wrap_content" android:layout_below="@+id/button4" android:id="@+id/checkBox1" android:layout_height="wrap_content" android:text="CheckBox" android:layout_toLeftOf="@+id/button4"></CheckBox>
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" android:id="@+id/button5" android:layout_alignParentRight="true"></Button>
+ </RelativeLayout>
+</LinearLayout>
+
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2.info
new file mode 100644
index 0000000..44d3b62
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2.info
@@ -0,0 +1,9 @@
+android.widget.LinearLayout [0,36,240,320] <LinearLayout>
+ android.widget.Button [0,0,49,36] <Button>
+ android.widget.RelativeLayout [0,36,240,284] <RelativeLayout>
+ android.widget.Button [0,0,49,36] <Button>
+ android.widget.Button [49,36,98,72] <Button>
+ android.widget.Button [98,72,147,108] <Button>
+ android.widget.CheckBox [18,108,98,144] <CheckBox>
+ android.widget.Button [191,0,240,36] <Button>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2.xml
new file mode 100644
index 0000000..0697c64
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample2.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <RelativeLayout android:layout_height="match_parent" android:id="@+id/relativeLayout1" android:layout_width="match_parent">
+ <Button android:layout_width="wrap_content" android:layout_alignParentLeft="true" android:id="@+id/button2" android:layout_height="wrap_content" android:text="Button"></Button>
+ <Button android:layout_width="wrap_content" android:id="@+id/button3" android:layout_below="@+id/button2" android:layout_height="wrap_content" android:text="Button" android:layout_toRightOf="@+id/button2"></Button>
+ <Button android:layout_width="wrap_content" android:id="@+id/button4" android:layout_below="@+id/button3" android:layout_height="wrap_content" android:text="Button" android:layout_toRightOf="@+id/button3"></Button>
+ <CheckBox android:layout_width="wrap_content" android:layout_below="@+id/button4" android:id="@+id/checkBox1" android:layout_height="wrap_content" android:text="CheckBox" android:layout_toLeftOf="@+id/button4"></CheckBox>
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" android:id="@+id/button5" android:layout_alignParentRight="true"></Button>
+ </RelativeLayout>
+</LinearLayout>
+
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-changeLayout3.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-changeLayout3.xml
new file mode 100644
index 0000000..c5118b6
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-changeLayout3.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <ImageView android:layout_alignParentLeft="true" android:layout_below="@+id/button1" android:layout_above="@+id/button2" android:id="@+id/android_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button" android:focusable="false" android:clickable="false" android:layout_weight="1.0" />
+ <Button android:layout_alignParentLeft="true" android:layout_alignParentBottom="true" android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+</RelativeLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract1.xml
new file mode 100644
index 0000000..70f576e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract1.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <ImageView android:id="@+id/android_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button" android:focusable="false" android:clickable="false" android:layout_weight="1.0" />
+ <include layout="@layout/newlayout1" android:id="@+id/button2_ref" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract2.xml
new file mode 100644
index 0000000..9a30a96
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract2.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <include layout="@layout/newlayout2" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract4.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract4.xml
new file mode 100644
index 0000000..445f88a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract4.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <include layout="@layout/newlayout3" android:id="@+id/android_logo_ref" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1.0"/>
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract5.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract5.xml
new file mode 100644
index 0000000..8df41ca
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-extract5.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <include layout="@layout/newlayout3" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn1.xml
new file mode 100644
index 0000000..5af18d6
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn1.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <ImageView android:id="@+id/android_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button" android:focusable="false" android:clickable="false" android:layout_weight="1.0" />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn2.xml
new file mode 100644
index 0000000..5d5fbfc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn2.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.gesture.GestureOverlayView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <ImageView android:id="@+id/android_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button" android:focusable="false" android:clickable="false" android:layout_weight="1.0" />
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ </LinearLayout>
+</android.gesture.GestureOverlayView>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn3.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn3.xml
new file mode 100644
index 0000000..efbeb18
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-expected-wrapIn3.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <ImageView android:id="@+id/android_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button" android:focusable="false" android:clickable="false" android:layout_weight="1.0" />
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1-expected-extract4.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1-expected-extract4.xml
new file mode 100644
index 0000000..d616269
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1-expected-extract4.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <LinearLayout android:id="@+id/newlinear2" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <include layout="@layout/newlayout3" android:id="@+id/android_logo_ref" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1.0"/>
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1-expected-extract5.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1-expected-extract5.xml
new file mode 100644
index 0000000..b37e7be
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1-expected-extract5.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <LinearLayout android:id="@+id/newlinear2" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <include layout="@layout/newlayout3" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1.xml
new file mode 100644
index 0000000..9cf3c43
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation1.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <LinearLayout android:id="@+id/newlinear2" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button"
+ android:focusable="false" android:clickable="false" android:layout_weight="1.0" android:id="@+id/android_logo" />
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2-expected-extract4.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2-expected-extract4.xml
new file mode 100644
index 0000000..40676ab
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2-expected-extract4.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:customprefix="http://schemas.android.com/apk/res/android" customprefix:id="@+id/newlinear" customprefix:orientation="vertical" customprefix:layout_width="match_parent" customprefix:layout_height="match_parent">
+ <include layout="@layout/newlayout3" customprefix:id="@+id/android_logo_ref" customprefix:layout_width="wrap_content" customprefix:layout_height="wrap_content" customprefix:layout_weight="1.0"/>
+ <Button customprefix:text="Button" customprefix:id="@+id/button1" customprefix:layout_width="wrap_content" customprefix:layout_height="wrap_content"></Button>
+ <Button customprefix:text="Button" customprefix:id="@+id/button2" customprefix:layout_width="wrap_content" customprefix:layout_height="wrap_content"></Button>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2-expected-extract5.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2-expected-extract5.xml
new file mode 100644
index 0000000..c51dc37
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2-expected-extract5.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:customprefix="http://schemas.android.com/apk/res/android" customprefix:id="@+id/newlinear" customprefix:orientation="vertical" customprefix:layout_width="match_parent" customprefix:layout_height="match_parent">
+ <include layout="@layout/newlayout3" customprefix:layout_width="wrap_content" customprefix:layout_height="wrap_content"/>
+ <Button customprefix:text="Button" customprefix:id="@+id/button2" customprefix:layout_width="wrap_content" customprefix:layout_height="wrap_content"></Button>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2.xml
new file mode 100644
index 0000000..48d4790
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3-variation2.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:customprefix="http://schemas.android.com/apk/res/android" customprefix:id="@+id/newlinear" customprefix:orientation="vertical" customprefix:layout_width="match_parent" customprefix:layout_height="match_parent">
+ <ImageView customprefix:id="@+id/android_logo" customprefix:layout_width="wrap_content"
+ customprefix:layout_height="wrap_content" customprefix:src="@drawable/android_button" customprefix:focusable="false" customprefix:clickable="false" customprefix:layout_weight="1.0" />
+ <Button customprefix:text="Button" customprefix:id="@+id/button1" customprefix:layout_width="wrap_content" customprefix:layout_height="wrap_content"></Button>
+ <Button customprefix:text="Button" customprefix:id="@+id/button2" customprefix:layout_width="wrap_content" customprefix:layout_height="wrap_content"></Button>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3.info
new file mode 100644
index 0000000..ef3324e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3.info
@@ -0,0 +1,5 @@
+android.widget.LinearLayout [0,36,240,320] <LinearLayout>
+ android.widget.Button [0,0,49,36] <Button>
+ android.widget.ImageView [0,36,128,248] <ImageView>
+ android.widget.Button [0,248,49,284] <Button>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3.xml
new file mode 100644
index 0000000..ddd136c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample3.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/newlinear" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <ImageView android:id="@+id/android_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android_button" android:focusable="false" android:clickable="false" android:layout_weight="1.0" />
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4-expected-changeLayout4.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4-expected-changeLayout4.xml
new file mode 100644
index 0000000..2dd7ab7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4-expected-changeLayout4.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout android:id="@+id/RelativeLayout1" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <CheckBox android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="CheckBox" android:id="@+id/checkBox1" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
+ <Button android:layout_below="@+id/checkBox1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" android:id="@+id/button5" android:layout_alignParentRight="true"></Button>
+</RelativeLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4.info
new file mode 100644
index 0000000..605c177
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4.info
@@ -0,0 +1,5 @@
+android.widget.LinearLayout [0,36,240,320] <LinearLayout>
+ android.widget.CheckBox [0,0,80,36] <CheckBox>
+ android.widget.RelativeLayout [0,36,240,284] <RelativeLayout>
+ android.widget.Button [191,0,240,36] <Button>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4.xml
new file mode 100644
index 0000000..a56f272
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample4.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <CheckBox android:text="CheckBox" android:id="@+id/checkBox1" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
+ <RelativeLayout android:layout_height="match_parent" android:id="@+id/relativeLayout1" android:layout_width="match_parent">
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" android:id="@+id/button5" android:layout_alignParentRight="true"></Button>
+ </RelativeLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5-expected-changeLayout5.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5-expected-changeLayout5.xml
new file mode 100644
index 0000000..225476c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5-expected-changeLayout5.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout android:id="@+id/RelativeLayout1" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <Button android:layout_centerHorizontal="true" android:layout_alignParentTop="true" android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"></Button>
+ <Button android:layout_centerHorizontal="true" android:layout_below="@+id/button1" android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"></Button>
+ <Button android:layout_alignParentRight="true" android:layout_below="@+id/button2" android:text="Button" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right"></Button>
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button4" android:layout_below="@+id/button3" android:layout_marginTop="70dp" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Button" android:id="@+id/button4" android:layout_width="wrap_content"></Button>
+ <Button android:layout_toRightOf="@+id/button4" android:layout_alignBaseline="@+id/button4" android:layout_alignTop="@+id/button4" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="Button" android:id="@+id/button5" android:layout_width="wrap_content"></Button>
+ <Button android:layout_toRightOf="@+id/button5" android:layout_alignParentBottom="true" android:layout_height="wrap_content" android:text="Button" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_gravity="bottom"></Button>
+</RelativeLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5.info
new file mode 100644
index 0000000..fce532e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5.info
@@ -0,0 +1,9 @@
+android.widget.LinearLayout [0,36,240,320] <LinearLayout>
+ android.widget.Button [95,0,144,36] <Button>
+ android.widget.Button [95,36,144,72] <Button>
+ android.widget.Button [191,72,240,108] <Button>
+ android.widget.LinearLayout [0,108,240,284] <LinearLayout>
+ android.widget.Button [0,70,49,106] <Button>
+ android.widget.Button [49,70,98,106] <Button>
+ android.widget.Button [98,140,147,176] <Button>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5.xml
new file mode 100644
index 0000000..afc1938
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample5.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"></Button>
+ <Button android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"></Button>
+ <Button android:text="Button" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right"></Button>
+ <LinearLayout android:id="@+id/linearLayout1" android:layout_width="match_parent" android:layout_height="match_parent">
+ <Button android:layout_height="wrap_content" android:layout_gravity="center" android:text="Button" android:id="@+id/button4" android:layout_width="wrap_content"></Button>
+ <Button android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="Button" android:id="@+id/button5" android:layout_width="wrap_content"></Button>
+ <Button android:layout_height="wrap_content" android:text="Button" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_gravity="bottom"></Button>
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6-expected-changeLayout6.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6-expected-changeLayout6.xml
new file mode 100644
index 0000000..045d2ce
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6-expected-changeLayout6.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/RelativeLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="true">
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button2" android:layout_alignParentTop="true" android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <RadioButton android:layout_toRightOf="@+id/button1" android:layout_alignBaseline="@+id/button2" android:layout_alignParentTop="true" android:text="RadioButton" android:id="@+id/radioButton1" android:layout_width="wrap_content" android:layout_height="wrap_content"></RadioButton>
+ <Button android:layout_toRightOf="@+id/radioButton1" android:layout_alignBaseline="@+id/button2" android:layout_alignParentTop="true" android:layout_width="wrap_content" android:id="@+id/button2" android:text="Button" android:layout_height="150dip"></Button>
+</RelativeLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6.info
new file mode 100644
index 0000000..7a7a44a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6.info
@@ -0,0 +1,5 @@
+android.widget.LinearLayout [0,36,240,320] <LinearLayout>
+ android.widget.Button [0,38,49,74] <Button>
+ android.widget.RadioButton [49,36,143,72] <RadioButton>
+ android.widget.Button [143,0,192,113] <Button>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6.xml
new file mode 100644
index 0000000..5cdc824
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample6.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="true">
+ <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <RadioButton android:text="RadioButton" android:id="@+id/radioButton1" android:layout_width="wrap_content" android:layout_height="wrap_content"></RadioButton>
+ <Button android:layout_width="wrap_content" android:id="@+id/button2" android:text="Button" android:layout_height="150dip"></Button>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7-expected-extract6.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7-expected-extract6.diff
new file mode 100644
index 0000000..636e301
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7-expected-extract6.diff
@@ -0,0 +1,10 @@
+< <LinearLayout android:layout_height="wrap_content" android:id="@+id/linearLayout4" android:layout_width="match_parent">
+< <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout5" android:layout_width="wrap_content">
+< <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout6" android:layout_width="wrap_content">
+< <!-- Comment -->
+< <Button android:text="Button" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+< </LinearLayout>
+< </LinearLayout>
+< </LinearLayout>
+---
+> <include layout="@layout/newlayout6" android:id="@+id/linearLayout4_ref" android:layout_width="match_parent" android:layout_height="wrap_content"/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7.info
new file mode 100644
index 0000000..134234c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7.info
@@ -0,0 +1,11 @@
+android.widget.LinearLayout [0,0,240,320] <LinearLayout>
+ android.widget.Button [0,0,101,36] <Button>
+ android.widget.LinearLayout [0,36,240,72] <LinearLayout>
+ android.widget.LinearLayout [0,0,73,36] <LinearLayout>
+ android.widget.LinearLayout [0,0,73,36] <LinearLayout>
+ android.widget.Button [0,0,73,36] <Button>
+ android.widget.LinearLayout [0,72,240,192] <LinearLayout>
+ android.widget.Button [0,0,117,36] <Button>
+ android.widget.Button [117,0,223,36] <Button>
+ android.widget.CheckBox [223,10,240,130] <CheckBox>
+ android.widget.Button [0,192,240,228] <Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7.xml
new file mode 100644
index 0000000..0445bbf
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample7.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout android:id="@+id/LinearLayout2" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
+ <Button android:text="FirstButton" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <LinearLayout android:layout_height="wrap_content" android:id="@+id/linearLayout4" android:layout_width="match_parent">
+ <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout5" android:layout_width="wrap_content">
+ <LinearLayout android:layout_height="match_parent" android:id="@+id/linearLayout6" android:layout_width="wrap_content">
+ <!-- Comment -->
+ <Button android:text="Button" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+ <LinearLayout android:layout_width="match_parent" android:id="@+id/linearLayout9" android:layout_height="wrap_content">
+ <Button android:text="SecondButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button2"></Button>
+ <Button android:text="ThirdButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button3"></Button>
+ <CheckBox android:id="@+id/checkBox1" android:text="CheckBox" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
+ </LinearLayout>
+ <Button android:layout_height="wrap_content" android:text="FourthButton" android:id="@+id/button4" android:layout_width="match_parent"></Button>
+</LinearLayout>
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8-expected-extract6.diff b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8-expected-extract6.diff
new file mode 100644
index 0000000..024141a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8-expected-extract6.diff
@@ -0,0 +1,15 @@
+< <LinearLayout android:layout_height="wrap_content"
+< android:id="@+id/linearLayout4" android:layout_width="match_parent">
+< <LinearLayout android:layout_height="match_parent"
+< android:layout_width="wrap_content" android:id="@+id/linearLayout5">
+< <LinearLayout android:layout_height="match_parent"
+< android:id="@+id/linearLayout6"
+< android:layout_width="wrap_content">
+< <Button android:text="Button" android:id="@+id/button6"
+< android:layout_width="wrap_content"
+< android:layout_height="wrap_content"></Button>
+< </LinearLayout>
+< </LinearLayout>
+< </LinearLayout>
+---
+> <include layout="@layout/newlayout6" android:id="@+id/linearLayout4_ref" android:layout_width="match_parent" android:layout_height="wrap_content"/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8.info b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8.info
new file mode 100644
index 0000000..ca294ba
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8.info
@@ -0,0 +1,12 @@
+android.widget.LinearLayout [0,0,240,320] <LinearLayout>
+ android.widget.Button [0,0,101,36] <Button>
+ android.widget.FrameLayout [0,36,240,72] <FrameLayout>
+ android.widget.LinearLayout [0,0,240,36] <LinearLayout>
+ android.widget.LinearLayout [0,0,73,36] <LinearLayout>
+ android.widget.LinearLayout [0,0,73,36] <LinearLayout>
+ android.widget.Button [0,0,73,36] <Button>
+ android.widget.FrameLayout [0,72,240,108] <FrameLayout>
+ android.widget.LinearLayout [0,0,240,36] <LinearLayout>
+ android.widget.LinearLayout [0,0,73,36] <LinearLayout>
+ android.widget.LinearLayout [0,0,73,36] <LinearLayout>
+ android.widget.Button [0,0,73,36] <Button>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8.xml
new file mode 100644
index 0000000..c798469
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/sample8.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout android:id="@+id/LinearLayout2"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent" android:layout_height="match_parent"
+ android:orientation="vertical">
+ <Button android:text="FirstButton" android:id="@+id/button1"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
+ <FrameLayout android:id="@+id/outer"
+ android:layout_width="match_parent" android:layout_height="wrap_content">
+ <LinearLayout android:layout_height="wrap_content"
+ android:id="@+id/linearLayout4" android:layout_width="match_parent">
+ <LinearLayout android:layout_height="match_parent"
+ android:layout_width="wrap_content" android:id="@+id/linearLayout5">
+ <LinearLayout android:layout_height="match_parent"
+ android:id="@+id/linearLayout6"
+ android:layout_width="wrap_content">
+ <Button android:text="Button" android:id="@+id/button6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"></Button>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+ </FrameLayout>
+ <FrameLayout android:id="@+id/outer"
+ android:layout_width="match_parent" android:layout_height="wrap_content">
+ <LinearLayout android:layout_height="wrap_content"
+ android:id="@+id/linearLayout4" android:layout_width="match_parent">
+ <LinearLayout android:layout_height="match_parent"
+ android:layout_width="wrap_content" android:id="@+id/linearLayout5">
+ <LinearLayout android:layout_height="match_parent"
+ android:id="@+id/linearLayout6"
+ android:layout_width="wrap_content">
+ <Button android:text="Button" android:id="@+id/button6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"></Button>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+ </FrameLayout>
+</LinearLayout>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfoTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfoTest.java
new file mode 100644
index 0000000..5eba812
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfoTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2011 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.editors.manifest;
+
+import static com.android.resources.ScreenSize.LARGE;
+import static com.android.resources.ScreenSize.NORMAL;
+import static com.android.resources.ScreenSize.XLARGE;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.NullProgressMonitor;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Map;
+
+public class ManifestInfoTest extends AdtProjectTest {
+ @Override
+ protected boolean testCaseNeedsUniqueProject() {
+ return true;
+ }
+
+ public void testGetActivityThemes1() throws Exception {
+ ManifestInfo info = getManifestInfo(
+ "<manifest xmlns:android='http://schemas.android.com/apk/res/android'\n" +
+ " package='com.android.unittest'>\n" +
+ " <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='4'/>\n" +
+ "</manifest>\n");
+ Map<String, String> map = info.getActivityThemes();
+ assertEquals(map.toString(), 0, map.size());
+ assertEquals("com.android.unittest", info.getPackage());
+ assertEquals("Theme", ResourceHelper.styleToTheme(info.getDefaultTheme(null, NORMAL)));
+ assertEquals("@android:style/Theme", info.getDefaultTheme(null, null));
+ assertEquals("Theme", ResourceHelper.styleToTheme(info.getDefaultTheme(null, XLARGE)));
+ }
+
+ public void testGetActivityThemes2() throws Exception {
+ ManifestInfo info = getManifestInfo(
+ "<manifest xmlns:android='http://schemas.android.com/apk/res/android'\n" +
+ " package='com.android.unittest'>\n" +
+ " <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='11'/>\n" +
+ "</manifest>\n");
+ Map<String, String> map = info.getActivityThemes();
+ assertEquals(map.toString(), 0, map.size());
+ assertEquals("com.android.unittest", info.getPackage());
+ assertEquals("Theme.Holo", ResourceHelper.styleToTheme(info.getDefaultTheme(null,
+ XLARGE)));
+ assertEquals("Theme", ResourceHelper.styleToTheme(info.getDefaultTheme(null, LARGE)));
+ }
+
+ public void testGetActivityThemes3() throws Exception {
+ ManifestInfo info = getManifestInfo(
+ "<manifest xmlns:android='http://schemas.android.com/apk/res/android'\n" +
+ " package='com.android.unittest'>\n" +
+ " <uses-sdk android:minSdkVersion='11'/>\n" +
+ "</manifest>\n");
+ Map<String, String> map = info.getActivityThemes();
+ assertEquals(map.toString(), 0, map.size());
+ assertEquals("com.android.unittest", info.getPackage());
+ assertEquals("Theme.Holo", ResourceHelper.styleToTheme(info.getDefaultTheme(null,
+ XLARGE)));
+ assertEquals("Theme", ResourceHelper.styleToTheme(info.getDefaultTheme(null, NORMAL)));
+ }
+
+ public void testGetActivityThemes4() throws Exception {
+ ManifestInfo info = getManifestInfo(
+ "<manifest xmlns:android='http://schemas.android.com/apk/res/android'\n" +
+ " package='com.android.unittest'>\n" +
+ " <application\n" +
+ " android:label='@string/app_name'\n" +
+ " android:name='.app.TestApp' android:icon='@drawable/app_icon'>\n" +
+ "\n" +
+ " <activity\n" +
+ " android:name='.prefs.PrefsActivity'\n" +
+ " android:label='@string/prefs_title' />\n" +
+ "\n" +
+ " <activity\n" +
+ " android:name='.app.IntroActivity'\n" +
+ " android:label='@string/intro_title'\n" +
+ " android:theme='@android:style/Theme.Dialog' />\n" +
+ " </application>\n" +
+ " <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='4'/>\n" +
+ "</manifest>\n" +
+ ""
+ );
+ assertEquals("com.android.unittest", info.getPackage());
+ assertEquals("Theme", ResourceHelper.styleToTheme(info.getDefaultTheme(null, XLARGE)));
+
+ Map<String, String> map = info.getActivityThemes();
+ assertEquals(map.toString(), 1, map.size());
+ assertNull(map.get("com.android.unittest.prefs.PrefsActivity"));
+ assertEquals("@android:style/Theme.Dialog",
+ map.get("com.android.unittest.app.IntroActivity"));
+ }
+
+ public void testGetActivityThemes5() throws Exception {
+ ManifestInfo info = getManifestInfo(
+ "<manifest xmlns:android='http://schemas.android.com/apk/res/android'\n" +
+ " package='com.android.unittest'" +
+ " android:theme='@style/NoBackground'>\n" +
+ " <application\n" +
+ " android:label='@string/app_name'\n" +
+ " android:name='.app.TestApp' android:icon='@drawable/app_icon'>\n" +
+ "\n" +
+ " <activity\n" +
+ " android:name='.prefs.PrefsActivity'\n" +
+ " android:label='@string/prefs_title' />\n" +
+ "\n" +
+ " <activity\n" +
+ " android:name='.app.IntroActivity'\n" +
+ " android:label='@string/intro_title'\n" +
+ " android:theme='@android:style/Theme.Dialog' />\n" +
+ " </application>\n" +
+ " <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='4'/>\n" +
+ "</manifest>\n" +
+ ""
+ );
+
+ assertEquals("@style/NoBackground", info.getDefaultTheme(null, XLARGE));
+ assertEquals("@style/NoBackground", info.getDefaultTheme(null, NORMAL));
+ assertEquals("NoBackground", ResourceHelper.styleToTheme(info.getDefaultTheme(null,
+ NORMAL)));
+
+ Map<String, String> map = info.getActivityThemes();
+ assertEquals(map.toString(), 1, map.size());
+ assertNull(map.get("com.android.unittest.prefs.PrefsActivity"));
+ assertEquals("@android:style/Theme.Dialog",
+ map.get("com.android.unittest.app.IntroActivity"));
+ }
+
+ public void testGetActivityThemes6() throws Exception {
+ // Ensures that when the *rendering* target is less than version 11, we don't
+ // use Holo even though the manifest SDK version calls for it.
+ ManifestInfo info = getManifestInfo(
+ "<manifest xmlns:android='http://schemas.android.com/apk/res/android'\n" +
+ " package='com.android.unittest'>\n" +
+ " <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='11'/>\n" +
+ "</manifest>\n");
+ Map<String, String> map = info.getActivityThemes();
+ assertEquals(map.toString(), 0, map.size());
+ assertEquals("com.android.unittest", info.getPackage());
+ assertEquals("Theme.Holo", ResourceHelper.styleToTheme(info.getDefaultTheme(null,
+ XLARGE)));
+
+ // Here's the check
+ IAndroidTarget olderVersion = new TestAndroidTarget(4);
+ assertEquals("Theme", ResourceHelper.styleToTheme(info.getDefaultTheme(olderVersion,
+ XLARGE)));
+
+ }
+
+ public void testGetApplicationLabelAndIcon() throws Exception {
+ ManifestInfo info = getManifestInfo(
+ "<manifest xmlns:android='http://schemas.android.com/apk/res/android'\n" +
+ " package='com.android.unittest'>\n" +
+ " <application android:icon=\"@drawable/icon\"\n" +
+ " android:label=\"@string/app_name\">\n" +
+ " </application>\n" +
+ "" +
+ "</manifest>\n");
+ Map<String, String> map = info.getActivityThemes();
+ assertEquals(map.toString(), 0, map.size());
+ assertEquals("com.android.unittest", info.getPackage());
+
+ assertEquals("Theme", ResourceHelper.styleToTheme(info.getDefaultTheme(null, NORMAL)));
+ assertEquals("@drawable/icon", info.getApplicationIcon());
+ assertEquals("@string/app_name", info.getApplicationLabel());
+ }
+
+ public void testGetApplicationNoLabelOrIcon() throws Exception {
+ ManifestInfo info = getManifestInfo(
+ "<manifest xmlns:android='http://schemas.android.com/apk/res/android'\n" +
+ " package='com.android.unittest'>\n" +
+ " <application>\n" +
+ " </application>\n" +
+ "" +
+ "</manifest>\n");
+ Map<String, String> map = info.getActivityThemes();
+ assertEquals(map.toString(), 0, map.size());
+ assertEquals("com.android.unittest", info.getPackage());
+
+ assertEquals("Theme", ResourceHelper.styleToTheme(info.getDefaultTheme(null, NORMAL)));
+ assertNull(info.getApplicationIcon());
+ assertNull(info.getApplicationLabel());
+ }
+
+ private ManifestInfo getManifestInfo(String manifestContents) throws Exception {
+ InputStream bstream = new ByteArrayInputStream(
+ manifestContents.getBytes("UTF-8")); //$NON-NLS-1$
+
+ IFile file = getProject().getFile("AndroidManifest.xml");
+ if (file.exists()) {
+ file.setContents(bstream, IFile.FORCE, new NullProgressMonitor());
+ } else {
+ file.create(bstream, false /* force */, new NullProgressMonitor());
+ }
+ return ManifestInfo.get(getProject());
+ }
+
+ private static class TestAndroidTarget implements IAndroidTarget {
+ private final int mApiLevel;
+
+ public TestAndroidTarget(int apiLevel) {
+ mApiLevel = apiLevel;
+ }
+
+ public boolean canRunOn(IAndroidTarget target) {
+ return false;
+ }
+
+ public String[] getAbiList() {
+ return null;
+ }
+
+ public String getClasspathName() {
+ return null;
+ }
+
+ public String getDefaultSkin() {
+ return null;
+ }
+
+ public String getDescription() {
+ return null;
+ }
+
+ public String getFullName() {
+ return null;
+ }
+
+ public String getImagePath(String abiType) {
+ return null;
+ }
+
+ public String getLocation() {
+ return null;
+ }
+
+ public String getName() {
+ return null;
+ }
+
+ public IOptionalLibrary[] getOptionalLibraries() {
+ return null;
+ }
+
+ public IAndroidTarget getParent() {
+ return null;
+ }
+
+ public String getPath(int pathId) {
+ return null;
+ }
+
+ public String[] getPlatformLibraries() {
+ return null;
+ }
+
+ public Map<String, String> getProperties() {
+ return null;
+ }
+
+ public String getProperty(String name) {
+ return null;
+ }
+
+ public Integer getProperty(String name, Integer defaultValue) {
+ return null;
+ }
+
+ public Boolean getProperty(String name, Boolean defaultValue) {
+ return null;
+ }
+
+ public int getRevision() {
+ return 0;
+ }
+
+ public String[] getSkins() {
+ return null;
+ }
+
+ public int getUsbVendorId() {
+ return 0;
+ }
+
+ public String getVendor() {
+ return null;
+ }
+
+ public AndroidVersion getVersion() {
+ return new AndroidVersion(mApiLevel, null);
+ }
+
+ public String getVersionName() {
+ return null;
+ }
+
+ public String hashString() {
+ return null;
+ }
+
+ public boolean isPlatform() {
+ return false;
+ }
+
+ public int compareTo(IAndroidTarget o) {
+ return 0;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/xml/HyperlinksTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/xml/HyperlinksTest.java
new file mode 100644
index 0000000..57ca80f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/xml/HyperlinksTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2010 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.editors.xml;
+
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
+import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks.ResourceLink;
+import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks.XmlResolver;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.internal.ErrorEditorPart;
+import org.eclipse.ui.internal.browser.WebBrowserEditor;
+import org.eclipse.wst.sse.ui.StructuredTextEditor;
+import org.eclipse.wst.sse.ui.internal.StructuredTextViewer;
+import org.eclipse.wst.xml.ui.internal.tabletree.XMLMultiPageEditorPart;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+@SuppressWarnings("restriction")
+public class HyperlinksTest extends AdtProjectTest {
+ @Override
+ protected boolean testCaseNeedsUniqueProject() {
+ return true;
+ }
+
+ public void testFqnRegexp() throws Exception {
+ assertTrue(Hyperlinks.CLASS_PATTERN.matcher("com.android.Foo").matches());
+ assertTrue(Hyperlinks.CLASS_PATTERN.matcher("com.android.pk_g.Foo_Bar1").
+ matches());
+ assertTrue(Hyperlinks.CLASS_PATTERN.matcher("com.android.Foo$Inner").matches());
+
+ // Should we allow non-standard packages and class names?
+ // For now, we're allowing it -- see how this works out in practice.
+ //assertFalse(XmlHyperlinkResolver.CLASS_PATTERN.matcher("Foo.bar").matches());
+ assertTrue(Hyperlinks.CLASS_PATTERN.matcher("Foo.bar").matches());
+
+ assertFalse(Hyperlinks.CLASS_PATTERN.matcher("LinearLayout").matches());
+ assertFalse(Hyperlinks.CLASS_PATTERN.matcher(".").matches());
+ assertFalse(Hyperlinks.CLASS_PATTERN.matcher(".F").matches());
+ assertFalse(Hyperlinks.CLASS_PATTERN.matcher("f.").matches());
+ assertFalse(Hyperlinks.CLASS_PATTERN.matcher("Foo").matches());
+ assertFalse(Hyperlinks.CLASS_PATTERN.matcher("com.android.1Foo").matches());
+ assertFalse(Hyperlinks.CLASS_PATTERN.matcher("1com.Foo").matches());
+ }
+
+ public void testNavigate1() throws Exception {
+ // Check navigating to a local resource
+ checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml",
+ "android:text=\"@string/app^_name\"");
+ }
+
+ public void testNavigate2() throws Exception {
+ // Check navigating to a framework resource
+ checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml",
+ "marginLeft=\"@android:dimen/app_ico^n_size\"");
+ }
+
+ public void testNavigate3() throws Exception {
+ // Check navigating to a style
+ checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml",
+ "style=\"@android:style/Widget.B^utton\"");
+ }
+
+ public void testNavigate4() throws Exception {
+ // Check navigating to resource with many resolutions
+ checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml",
+ "android:text=\"@android:st^ring/ok\"");
+ }
+
+ public void testNavigate5() throws Exception {
+ // Check navigating to styles
+ checkXmlNavigation("navigationstyles.xml", "res/values/navigationstyles.xml",
+ "parent=\"android:Theme.Li^ght\">");
+ }
+
+ public void testNavigate6() throws Exception {
+ // Check navigating to a portion of a style (this should pick android:Theme, not
+ // android:Theme.Light
+ checkXmlNavigation("navigationstyles.xml", "res/values/navigationstyles.xml",
+ "parent=\"android:The^me.Light\">");
+ }
+
+ public void testNavigate7() throws Exception {
+ // Check navigating to a resource inside text content
+ checkXmlNavigation("navigationstyles.xml", "res/values/navigationstyles.xml",
+ "popupBackground\">@android:drawable/spinner_dr^opdown_background</item>");
+ }
+
+ public void testNavigate8() throws Exception {
+ // Check navigating to a resource inside text content where there is space around
+ // the URL
+ checkXmlNavigation("navigationstyles.xml", "res/values/navigationstyles.xml",
+ "colorBackground\"> @color/cust^om_theme_color </item>");
+ }
+
+ public void testNavigate9a() throws Exception {
+ // Check navigating to a an activity
+ checkXmlNavigation("manifest.xml", "AndroidManifest.xml",
+ "<activity android:name=\".Test^Activity\"");
+ }
+
+ /* Not yet implemented
+ public void testNavigate9b() throws Exception {
+ // Check navigating to a an activity - clicking on the activity element should
+ // work too
+ checkXmlNavigation("manifest.xml", "AndroidManifest.xml",
+ "<acti^vity android:name=\".TestActivity\"");
+ }
+ */
+
+ public void testNavigate10() throws Exception {
+ // Check navigating to a permission
+ checkXmlNavigation("manifest.xml", "AndroidManifest.xml",
+ "<uses-permission android:name=\"android.permission.AC^CESS_NETWORK_STATE\" />");
+ }
+
+ public void testNavigate11a() throws Exception {
+ // Check navigating to an intent
+ checkXmlNavigation("manifest.xml", "AndroidManifest.xml",
+ "<action android:name=\"android.intent.ac^tion.MAIN\" />");
+ }
+
+ public void testNavigate11g() throws Exception {
+ // Check navigating to an intent
+ checkXmlNavigation("manifest.xml", "AndroidManifest.xml",
+ "<category android:name=\"android.intent.category.LA^UNCHER\" />");
+ }
+
+ public void testNavigate12() throws Exception {
+ // Check navigating to a custom view class
+ checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml",
+ "<my.Cust^omView></my.CustomView>");
+ }
+
+ // Left to test:
+ // onClick handling
+ // class attributes
+ // Test that the correct file is actually opened!
+
+ private void checkXmlNavigation(String basename, String targetPath,
+ String caretLocation) throws Exception {
+ IFile file = getTestDataFile(getProject(), basename, targetPath, true);
+
+ // Determine the offset
+ int offset = getCaretOffset(file, caretLocation);
+
+ // Open file
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ assertNotNull(page);
+ IEditorPart editor = IDE.openEditor(page, file);
+ assertTrue(editor.getClass().getName(), editor instanceof AndroidXmlEditor);
+ AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor;
+ ISourceViewer viewer = layoutEditor.getStructuredSourceViewer();
+
+ XmlResolver resolver = new Hyperlinks.XmlResolver();
+ IHyperlink[] links = resolver.detectHyperlinks(viewer, new Region(offset, 0), true);
+ assertNotNull(links);
+
+ StringBuilder sb = new StringBuilder(1000);
+ sb.append("Go To Declaration in " + basename + " for " + caretLocation + ":\n");
+ for (IHyperlink link : links) {
+ sb.append(link.getHyperlinkText());
+ sb.append(" : ");
+ sb.append(" [");
+ IRegion region = link.getHyperlinkRegion();
+ sb.append(viewer.getDocument().get(region.getOffset(), region.getLength()));
+ sb.append("]");
+ if (link instanceof Hyperlinks.ResourceLink) {
+ Hyperlinks.ResourceLink resourceLink = (ResourceLink) link;
+ sb.append("\n ");
+ ResourceFile resourceFile = resourceLink.getFile();
+ String desc = resourceFile.toString();
+ desc = desc.replace('\\', '/');
+ // For files in the SDK folder, strip out file prefix
+ int dataRes = desc.indexOf("data/res");
+ if (dataRes != -1) {
+ desc = desc.substring(dataRes);
+ }
+ desc = removeSessionData(desc);
+ sb.append(desc);
+ }
+ sb.append('\n');
+ }
+
+ // Open the first link
+ IHyperlink link = links[0];
+ link.open();
+ IEditorPart newEditor = Hyperlinks.getEditor();
+ // Ensure that this isn't an invalid file (e.g. opening the SDK platform files
+ // with incorrect content binding could cause this)
+ assertTrue(!(newEditor instanceof ErrorEditorPart));
+
+ IDocument document = null;
+ Point selection = null;
+
+ if (newEditor instanceof AndroidXmlEditor) {
+ AndroidXmlEditor xmlEditor = (AndroidXmlEditor) newEditor;
+ document = xmlEditor.getStructuredSourceViewer().getDocument();
+ selection = xmlEditor.getStructuredSourceViewer().getSelectedRange();
+ } else if (newEditor instanceof XMLMultiPageEditorPart) {
+ XMLMultiPageEditorPart xmlEditor = (XMLMultiPageEditorPart) newEditor;
+ Field field = xmlEditor.getClass().getDeclaredField("fTextEditor");
+ field.setAccessible(true);
+ StructuredTextEditor ste = (StructuredTextEditor) field.get(newEditor);
+ if (ste == null) {
+ Method method = xmlEditor.getClass().getMethod("getTextEditor", new Class[0]);
+ ste = (StructuredTextEditor) method.invoke(newEditor, new Object[0]);
+ }
+ StructuredTextViewer textViewer = ste.getTextViewer();
+ document = textViewer.getDocument();
+ selection = textViewer.getSelectedRange();
+ } else if (newEditor instanceof WebBrowserEditor) {
+ WebBrowserEditor browser = (WebBrowserEditor) newEditor;
+ Field field = browser.getClass().getDeclaredField("initialURL");
+ field.setAccessible(true);
+ String initialUrl = (String) field.get(newEditor);
+ int index = initialUrl.indexOf("reference");
+ if (index != -1) {
+ initialUrl = initialUrl.substring(index);
+ }
+ initialUrl = initialUrl.replace('\\', '/');
+ sb.append("\n\nAfter open, a browser is shown with this URL:\n");
+ sb.append(" ");
+ sb.append(initialUrl);
+ sb.append("\n");
+ } else {
+ fail("Unhandled editor type: " + newEditor.getClass().getName());
+ return;
+ }
+
+ if (document != null && selection != null) {
+ int lineStart = document.getLineInformationOfOffset(selection.x).getOffset();
+ IRegion lineEndInfo = document.getLineInformationOfOffset(selection.x + selection.y);
+ int lineEnd = lineEndInfo.getOffset() + lineEndInfo.getLength();
+ String text = document.get(lineStart, lineEnd - lineStart);
+ int selectionStart = selection.x - lineStart;
+ int selectionEnd = selectionStart + selection.y;
+ if (selectionEnd > selectionStart) {
+ // Selection range
+ text = text.substring(0, selectionStart) + "[^" +
+ text.substring(selectionStart, selectionEnd) + "]" +
+ text.substring(selectionEnd);
+ } else {
+ text = text.substring(0, selectionStart) + "^" +
+ text.substring(selectionStart);
+ }
+ text = removeSessionData(text);
+
+ sb.append("\n\nAfter open, the selected text is:\n");
+ sb.append(text);
+ sb.append("\n");
+ }
+
+ assertEqualsGolden(basename, sb.toString(), "txt");
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/wizards/newproject/StubProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/wizards/newproject/StubProjectCreationPage.java
index 0ed9ed4..f524d7f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/wizards/newproject/StubProjectCreationPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/wizards/newproject/StubProjectCreationPage.java
@@ -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
@@ -20,6 +20,7 @@ import com.android.sdklib.IAndroidTarget;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
+import org.eclipse.ui.IWorkingSet;
/**
* Stub class for project creation page.
@@ -87,6 +88,10 @@ public class StubProjectCreationPage extends NewProjectCreationPage {
public boolean useDefaultLocation() {
return false;
}
+
+ public IWorkingSet[] getSelectedWorkingSets() {
+ return new IWorkingSet[0];
+ }
};
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java
index 281170e..b4420e4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java
@@ -15,7 +15,9 @@
*/
package com.android.ide.eclipse.tests;
-import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.sdklib.SdkConstants;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Platform;
@@ -36,6 +38,10 @@ public class AdtTestData {
private static AdtTestData sInstance = null;
private static final Logger sLogger = Logger.getLogger(AdtTestData.class.getName());
+ /** The prefered directory separator to use. */
+ private static final String DIR_SEP_STR = "/";
+ private static final char DIR_SEP_CHAR = '/';
+
/** The absolute file path to the plugin's contents. */
private String mOsRootDataPath;
@@ -54,6 +60,20 @@ public class AdtTestData {
sLogger.info("Running as an Eclipse Plug-in JUnit test, using FileLocator");
try {
mOsRootDataPath = FileLocator.resolve(url).getFile();
+ if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS) {
+ // Fix the path returned by the URL resolver
+ // so that it actually works on Windows.
+
+ // First, Windows paths don't start with a / especially
+ // if they contain a drive spec such as C:/...
+ int pos = mOsRootDataPath.indexOf(':');
+ if (pos > 0 && mOsRootDataPath.charAt(0) == '/') {
+ mOsRootDataPath = mOsRootDataPath.substring(1);
+ }
+
+ // Looking for "." probably inserted a /./, so clean it up
+ mOsRootDataPath = mOsRootDataPath.replace("/./", "/");
+ }
} catch (IOException e) {
sLogger.warning("IOException while using FileLocator, reverting to url");
mOsRootDataPath = url.getFile();
@@ -64,13 +84,18 @@ public class AdtTestData {
}
}
- if (mOsRootDataPath.equals(AndroidConstants.WS_SEP)) {
+ if (mOsRootDataPath.equals(AdtConstants.WS_SEP)) {
sLogger.warning("Resource data not found using class loader!, Defaulting to no path");
}
- if (!mOsRootDataPath.endsWith(File.separator)) {
+ if (File.separatorChar == '\\') {
+ // On Windows, uniformize all separators to use the / convention
+ mOsRootDataPath.replace('\\', DIR_SEP_CHAR);
+ }
+
+ if (!mOsRootDataPath.endsWith(File.separator) && !mOsRootDataPath.endsWith(DIR_SEP_STR)) {
sLogger.info("Fixing test_data env variable (does not end with path separator)");
- mOsRootDataPath = mOsRootDataPath.concat(File.separator);
+ mOsRootDataPath += DIR_SEP_STR;
}
}
@@ -91,6 +116,20 @@ public class AdtTestData {
* @return absolute OS path to test file
*/
public String getTestFilePath(String osRelativePath) {
- return mOsRootDataPath + osRelativePath;
+ File path = new File(mOsRootDataPath, osRelativePath);
+
+ if (!path.exists()) {
+ // On Windows at least this ends up using the wrong plugin path.
+ String pkgAdt = AdtPlugin .class.getPackage().getName();
+ String pkgTests = AdtTestData.class.getPackage().getName();
+
+ if (mOsRootDataPath.contains(pkgAdt)) {
+ path = new File(mOsRootDataPath.replace(pkgAdt, pkgTests), osRelativePath);
+ }
+
+ assert path.exists();
+ }
+
+ return path.getAbsolutePath();
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java
index 2774557..d8692ae 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java
@@ -17,6 +17,7 @@ package com.android.ide.eclipse.tests;
import com.android.ide.common.sdk.LoadStatus;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetParser;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
@@ -60,6 +61,13 @@ public abstract class SdkTestCase extends TestCase {
return null;
}
+ // We'll never break out of the SDK load-wait-loop if the AdtPlugin doesn't
+ // actually have a valid SDK location because it won't have started an async load:
+ String sdkLocation = AdtPrefs.getPrefs().getOsSdkFolder();
+ assertTrue("No valid SDK installation is set; for tests you typically need to set the"
+ + " environment variable ADT_TEST_SDK_PATH to point to an SDK folder",
+ sdkLocation != null && sdkLocation.length() > 0);
+
Object sdkLock = Sdk.getLock();
LoadStatus loadStatus = LoadStatus.LOADING;
// wait for ADT to load the SDK on a separate thread
@@ -85,6 +93,10 @@ public abstract class SdkTestCase extends TestCase {
return sdk;
}
+ protected boolean validateSdk(IAndroidTarget target) {
+ return true;
+ }
+
/**
* Checks that the provided sdk contains one or more valid targets.
* @param sdk the {@link Sdk} to validate.
@@ -92,6 +104,9 @@ public abstract class SdkTestCase extends TestCase {
private void validateSdk(Sdk sdk) {
assertTrue("sdk has no targets", sdk.getTargets().length > 0);
for (IAndroidTarget target : sdk.getTargets()) {
+ if (!validateSdk(target)) {
+ continue;
+ }
IStatus status = new AndroidTargetParser(target).run(new NullProgressMonitor());
if (status.getCode() != IStatus.OK) {
fail("Failed to parse targets data");
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
index f18ec36..8b71c5a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
@@ -16,29 +16,34 @@
package com.android.ide.eclipse.tests.functests.layoutRendering;
+import com.android.AndroidConstants;
import com.android.ide.common.rendering.LayoutLibrary;
+import com.android.ide.common.rendering.api.AdapterBinding;
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
+import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
+import com.android.ide.common.resources.configuration.PixelDensityQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
+import com.android.ide.common.resources.configuration.TouchScreenQualifier;
import com.android.ide.common.sdk.LoadStatus;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
-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.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.tests.SdkTestCase;
+import com.android.io.FolderWrapper;
import com.android.resources.Density;
import com.android.resources.Keyboard;
import com.android.resources.KeyboardState;
@@ -50,7 +55,6 @@ import com.android.resources.ScreenSize;
import com.android.resources.TouchScreen;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.FolderWrapper;
import com.android.util.Pair;
import org.kxml2.io.KXmlParser;
@@ -125,6 +129,21 @@ public class ApiDemosRenderingTest extends SdkTestCase {
return null;
}
+ public ILayoutPullParser getParser(String layoutName) {
+ return null;
+ }
+
+ public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
+ ResourceReference itemRef, int fullPosition, int typePosition,
+ int fullChildPosition, int typeChildPosition,
+ ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue) {
+ return null;
+ }
+
+ public AdapterBinding getAdapterBinding(ResourceReference adapterView,
+ Object adapterCookie, Object viewObject) {
+ return null;
+ }
}
public void testApiDemos() throws IOException, XmlPullParserException {
@@ -168,13 +187,13 @@ public class ApiDemosRenderingTest extends SdkTestCase {
}
// look for the layout folder
- File layoutFolder = new File(resFolder, SdkConstants.FD_LAYOUT);
+ File layoutFolder = new File(resFolder, AndroidConstants.FD_RES_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);
+ ResourceRepository framework = ResourceManager.getInstance().loadFrameworkResources(target);
// now load the project resources
ProjectResources project = new ProjectResources(null /*project*/);
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java
index f4092eb..62725b8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java
@@ -60,8 +60,8 @@ public class AbsoluteLayoutRuleTest extends LayoutTestBase {
// Drop preview
"useStyle(DROP_PREVIEW), drawRect(Rect[30,-10,105,80])");
- assertEquals("30dip", inserted.getStringAttr(ANDROID_URI, "layout_x"));
- assertEquals("-10dip", inserted.getStringAttr(ANDROID_URI, "layout_y"));
+ assertEquals("30dp", inserted.getStringAttr(ANDROID_URI, "layout_x"));
+ assertEquals("-10dp", inserted.getStringAttr(ANDROID_URI, "layout_y"));
// Without drag bounds we should just draw guide lines instead
inserted = dragInto(new Rect(0, 0, 0, 0), new Point(30, -10), 4, -1,
@@ -70,8 +70,8 @@ public class AbsoluteLayoutRuleTest extends LayoutTestBase {
"useStyle(GUIDELINE), drawLine(30,0,30,480), drawLine(0,-10,240,-10)",
// Drop preview
"useStyle(DROP_PREVIEW), drawLine(30,-10,240,-10), drawLine(30,-10,30,480)");
- assertEquals("30dip", inserted.getStringAttr(ANDROID_URI, "layout_x"));
- assertEquals("-10dip", inserted.getStringAttr(ANDROID_URI, "layout_y"));
+ assertEquals("30dp", inserted.getStringAttr(ANDROID_URI, "layout_x"));
+ assertEquals("-10dp", inserted.getStringAttr(ANDROID_URI, "layout_y"));
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java
index 5b79ac3..18d985e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java
@@ -39,7 +39,7 @@ import junit.framework.TestCase;
/**
* Common layout helpers from LayoutRule tests
*/
-public abstract class LayoutTestBase extends TestCase {
+public class LayoutTestBase extends TestCase {
/**
* Helper function used by tests to drag a button into a canvas containing
* the given children.
@@ -244,6 +244,15 @@ public abstract class LayoutTestBase extends TestCase {
fail("Not supported in tests yet");
return null;
}
+
+ public String displayIncludeSourceInput() {
+ fail("Not supported in tests yet");
+ return null;
+ }
+
+ public void select(Collection<INode> nodes) {
+ fail("Not supported in tests yet");
+ }
}
public void testDummy() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java
index 14430a5..d5f1ae9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java
@@ -154,6 +154,13 @@ public class TestNode implements INode {
return child;
}
+ public void removeChild(INode node) {
+ int index = mChildren.indexOf(node);
+ if (index != -1) {
+ removeChild(index);
+ }
+ }
+
public boolean setAttribute(String uri, String localName, String value) {
mAttributes.put(uri + localName, new TestAttribute(uri, localName, value));
return true;
@@ -164,4 +171,5 @@ public class TestNode implements INode {
return "TestNode [fqn=" + mFqcn + ", infos=" + mAttributeInfos
+ ", attributes=" + mAttributes + ", bounds=" + mBounds + "]";
}
+
} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java
index 5ccb494..580fbaa 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java
@@ -65,6 +65,7 @@ public class DescriptorsUtilsTest extends TestCase {
assertEquals("Capital", DescriptorsUtils.capitalize("Capital"));
assertEquals("CamelCase", DescriptorsUtils.capitalize("camelCase"));
assertEquals("", DescriptorsUtils.capitalize(""));
+ assertSame("Foo", DescriptorsUtils.capitalize("Foo"));
}
public void testFormatTooltip() {
@@ -240,15 +241,9 @@ public class DescriptorsUtilsTest extends TestCase {
}
}
- public void testToXmlAttributeValue() throws Exception {
- assertEquals("", DescriptorsUtils.toXmlAttributeValue(""));
- assertEquals("foo", DescriptorsUtils.toXmlAttributeValue("foo"));
- assertEquals("foo<bar", DescriptorsUtils.toXmlAttributeValue("foo<bar"));
-
- assertEquals("&quot;", DescriptorsUtils.toXmlAttributeValue("\""));
- assertEquals("&apos;", DescriptorsUtils.toXmlAttributeValue("'"));
- assertEquals("foo&quot;b&apos;&apos;ar",
- DescriptorsUtils.toXmlAttributeValue("foo\"b''ar"));
+ public void testGetBasename() {
+ assertEquals("Foo", DescriptorsUtils.getBasename("Foo"));
+ assertEquals("Foo", DescriptorsUtils.getBasename("foo.Foo"));
+ assertEquals("String", DescriptorsUtils.getBasename("java.util.String"));
}
-
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java
index a240f90..2df472e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java
@@ -17,6 +17,7 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import com.android.ide.common.rendering.api.Capability;
+import com.android.ide.common.rendering.api.DataBindingItem;
import com.android.ide.common.rendering.api.MergeCookie;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
@@ -40,7 +41,7 @@ import junit.framework.TestCase;
public class CanvasViewInfoTest extends TestCase {
- private static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) {
+ public static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) {
if (hasChildren) {
return new ViewElementDescriptor(name, name, fqn, "", "", new AttributeDescriptor[0],
new AttributeDescriptor[0], new ElementDescriptor[1], false);
@@ -49,7 +50,7 @@ public class CanvasViewInfoTest extends TestCase {
}
}
- private static UiViewElementNode createNode(UiViewElementNode parent, String fqn,
+ public static UiViewElementNode createNode(UiViewElementNode parent, String fqn,
boolean hasChildren) {
String name = fqn.substring(fqn.lastIndexOf('.') + 1);
ViewElementDescriptor descriptor = createDesc(name, fqn, hasChildren);
@@ -60,11 +61,20 @@ public class CanvasViewInfoTest extends TestCase {
return (UiViewElementNode) parent.appendNewUiChild(descriptor);
}
- private static UiViewElementNode createNode(String fqn, boolean hasChildren) {
+ public static UiViewElementNode createNode(String fqn, boolean hasChildren) {
return createNode(null, fqn, hasChildren);
}
public void testNormalCreate() throws Exception {
+ normal(true);
+ }
+
+ public void testNormalCreateLayoutLib5() throws Exception {
+ normal(false);
+ }
+
+ private void normal(boolean layoutlib5) {
+
// Normal view hierarchy, no null keys anywhere
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
@@ -75,7 +85,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child2 = new ViewInfo("Button", child2Node, 0, 20, 70, 25);
root.setChildren(Arrays.asList(child1, child2));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -100,6 +110,15 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testShowIn() throws Exception {
+ showIn(false);
+ }
+
+ public void testShowInLayoutLib5() throws Exception {
+ showIn(true);
+ }
+
+ public void showIn(boolean layoutlib5) throws Exception {
+
// Test rendering of "Show Included In" (included content rendered
// within an outer content that has null keys)
@@ -112,7 +131,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", child21Node, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -137,6 +156,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testIncludeTag() throws Exception {
+ boolean layoutlib5 = true;
+
// Test rendering of included views on layoutlib 5+ (e.g. has <include> tag)
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
@@ -149,7 +170,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -176,9 +197,10 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testNoIncludeTag() throws Exception {
+ boolean layoutlib5 = false;
+
// Test rendering of included views on layoutlib 4- (e.g. no <include> tag cookie
- // in
- // view info)
+ // in view info)
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100);
@@ -190,7 +212,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -217,6 +239,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testMergeMatching() throws Exception {
+ boolean layoutlib5 = false;
+
// Test rendering of MULTIPLE included views or when there is no simple match
// between view info and ui element node children
@@ -232,7 +256,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -272,6 +296,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testMerge() throws Exception {
+ boolean layoutlib5 = false;
+
// Test rendering of MULTIPLE included views or when there is no simple match
// between view info and ui element node children
@@ -286,7 +312,7 @@ public class CanvasViewInfoTest extends TestCase {
ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25);
child2.setChildren(Arrays.asList(child21));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect());
@@ -313,6 +339,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testInsertMerge() throws Exception {
+ boolean layoutlib5 = false;
+
// Test rendering of MULTIPLE included views or when there is no simple match
// between view info and ui element node children
@@ -320,7 +348,7 @@ public class CanvasViewInfoTest extends TestCase {
UiViewElementNode rootNode = createNode(mergeNode, "android.widget.Button", false);
ViewInfo root = new ViewInfo("Button", rootNode, 10, 10, 100, 100);
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("merge", rootView.getName());
assertSame(rootView.getUiViewNode(), mergeNode);
@@ -340,6 +368,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testUnmatchedMissing() throws Exception {
+ boolean layoutlib5 = false;
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100);
List<ViewInfo> children = new ArrayList<ViewInfo>();
@@ -387,7 +417,7 @@ public class CanvasViewInfoTest extends TestCase {
}
root.setChildren(children);
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
// dump(root, 0);
@@ -412,6 +442,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testMergeCookies() throws Exception {
+ boolean layoutlib5 = true;
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100);
@@ -431,7 +463,7 @@ public class CanvasViewInfoTest extends TestCase {
}
root.setChildren(children);
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("LinearLayout", rootView.getName());
@@ -446,6 +478,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testMergeCookies2() throws Exception {
+ boolean layoutlib5 = true;
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100);
@@ -460,12 +494,13 @@ public class CanvasViewInfoTest extends TestCase {
ArrayList<ViewInfo> children = new ArrayList<ViewInfo>();
for (int i = 0; i < 10; i++) {
Object cookie = (i % 2) == 0 ? cookie1 : cookie2;
- ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50, (i + 1) * 20);
+ ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50,
+ (i + 1) * 20);
children.add(childView);
}
root.setChildren(children);
- Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root);
+ Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5);
CanvasViewInfo rootView = result.getFirst();
List<Rectangle> bounds = result.getSecond();
assertNull(bounds);
@@ -495,6 +530,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testIncludeBounds() throws Exception {
+ boolean layoutlib5 = true;
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100);
@@ -509,12 +546,13 @@ public class CanvasViewInfoTest extends TestCase {
ArrayList<ViewInfo> children = new ArrayList<ViewInfo>();
for (int i = 0; i < 10; i++) {
Object cookie = (i % 2) == 0 ? cookie1 : cookie2;
- ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50, (i + 1) * 20);
+ ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50,
+ (i + 1) * 20);
children.add(childView);
}
root.setChildren(children);
- Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root);
+ Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5);
CanvasViewInfo rootView = result.getFirst();
List<Rectangle> bounds = result.getSecond();
assertNotNull(rootView);
@@ -548,21 +586,27 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testIncludeBounds2() throws Exception {
+ includeBounds2(false);
+ }
+
+ public void testIncludeBounds2LayoutLib5() throws Exception {
+ includeBounds2(true);
+ }
+
+ public void includeBounds2(boolean layoutlib5) throws Exception {
+
UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true);
ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100);
UiViewElementNode node1 = createNode(rootNode, "childNode1", false);
UiViewElementNode node2 = createNode(rootNode, "childNode2", false);
- // Sets alternating merge cookies and checks whether the node sibling lists are
- // okay and merged correctly
-
ViewInfo childView1 = new ViewInfo("childView1", node1, 0, 20, 50, 40);
ViewInfo childView2 = new ViewInfo("childView2", node2, 0, 40, 50, 60);
root.setChildren(Arrays.asList(childView1, childView2));
- Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root);
+ Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5);
CanvasViewInfo rootView = result.getFirst();
List<Rectangle> bounds = result.getSecond();
assertNotNull(rootView);
@@ -580,6 +624,8 @@ public class CanvasViewInfoTest extends TestCase {
}
public void testGestureOverlayView() throws Exception {
+ boolean layoutlib5 = true;
+
// Test rendering of included views on layoutlib 5+ (e.g. has <include> tag)
UiViewElementNode rootNode = createNode("android.gesture.GestureOverlayView", true);
@@ -590,7 +636,7 @@ public class CanvasViewInfoTest extends TestCase {
root.setChildren(Collections.singletonList(child));
ViewInfo grandChild = new ViewInfo("Button", grandChildNode, 0, 20, 70, 25);
child.setChildren(Collections.singletonList(grandChild));
- CanvasViewInfo rootView = CanvasViewInfo.create(root).getFirst();
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst();
assertNotNull(rootView);
assertEquals("GestureOverlayView", rootView.getName());
@@ -611,6 +657,46 @@ public class CanvasViewInfoTest extends TestCase {
assertFalse(grandChildView.isRoot());
}
+ public void testListView() throws Exception {
+ // For ListViews we get AdapterItemReferences as cookies. Ensure that this
+ // works properly.
+ //
+ // android.widget.FrameLayout [0,50,320,480] <FrameLayout>
+ // android.widget.ListView [0,0,320,430] <ListView>
+ // android.widget.LinearLayout [0,0,320,17] SessionParams$AdapterItemReference
+ // android.widget.TextView [0,0,73,17]
+ // android.widget.LinearLayout [0,18,320,35] SessionParams$AdapterItemReference
+ // android.widget.TextView [0,0,73,17]
+ // android.widget.LinearLayout [0,36,320,53] SessionParams$AdapterItemReference
+ // android.widget.TextView [0,0,73,17]
+ // ...
+
+ UiViewElementNode rootNode = createNode("FrameLayout", true);
+ UiViewElementNode childNode = createNode(rootNode, "ListView", false);
+ /*UiViewElementNode grandChildNode =*/ createNode(childNode, "LinearLayout", false);
+ /*UiViewElementNode greatGrandChildNode =*/ createNode(childNode, "TextView", false);
+ DataBindingItem dataBindingItem = new DataBindingItem("foo");
+
+ ViewInfo root = new ViewInfo("FrameLayout", rootNode, 0, 50, 320, 480);
+ ViewInfo child = new ViewInfo("ListView", childNode, 0, 0, 320, 430);
+ root.setChildren(Collections.singletonList(child));
+ ViewInfo grandChild = new ViewInfo("LinearLayout", dataBindingItem, 0, 0, 320, 17);
+ child.setChildren(Collections.singletonList(grandChild));
+ ViewInfo greatGrandChild = new ViewInfo("Button", null, 0, 0, 73, 17);
+ grandChild.setChildren(Collections.singletonList(greatGrandChild));
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, true /*layoutlib5*/).getFirst();
+ assertNotNull(rootView);
+
+ assertEquals("FrameLayout", rootView.getName());
+ assertEquals(1, rootView.getChildren().size());
+ assertSame(rootNode, rootView.getUiViewNode());
+
+ CanvasViewInfo childView = rootView.getChildren().get(0);
+ assertEquals("ListView", childView.getName());
+ assertEquals(0, childView.getChildren().size());
+ assertSame(childNode, childView.getUiViewNode());
+ }
+
/**
* Dumps out the given {@link ViewInfo} hierarchy to standard out.
* Useful during development.
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilitiesTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilitiesTest.java
new file mode 100644
index 0000000..049e1cc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilitiesTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.gle2;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.Arrays;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import junit.framework.TestCase;
+
+public class DomUtilitiesTest extends TestCase {
+
+ public void testToXmlAttributeValue() throws Exception {
+ assertEquals("", DomUtilities.toXmlAttributeValue(""));
+ assertEquals("foo", DomUtilities.toXmlAttributeValue("foo"));
+ assertEquals("foo<bar", DomUtilities.toXmlAttributeValue("foo<bar"));
+
+ assertEquals("&quot;", DomUtilities.toXmlAttributeValue("\""));
+ assertEquals("&apos;", DomUtilities.toXmlAttributeValue("'"));
+ assertEquals("foo&quot;b&apos;&apos;ar",
+ DomUtilities.toXmlAttributeValue("foo\"b''ar"));
+ }
+
+ public void testIsEquivalent() throws Exception {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document1 = builder.newDocument();
+ Document document2 = builder.newDocument();
+ document1.appendChild(document1.createElement("root"));
+ document2.appendChild(document2.createElement("root"));
+
+ assertFalse(DomUtilities.isEquivalent(null, null));
+ Element root1 = document1.getDocumentElement();
+ assertFalse(DomUtilities.isEquivalent(null, root1));
+ Element root2 = document2.getDocumentElement();
+ assertFalse(DomUtilities.isEquivalent(root2, null));
+ assertTrue(DomUtilities.isEquivalent(root1, root2));
+
+ root1.appendChild(document1.createTextNode(" "));
+ // Differences in text are NOT significant!
+ assertTrue(DomUtilities.isEquivalent(root1, root2));
+ root2.appendChild(document2.createTextNode(" "));
+ assertTrue(DomUtilities.isEquivalent(root1, root2));
+
+ Element foo1 = document1.createElement("foo");
+ Element foo2 = document2.createElement("foo");
+ root1.appendChild(foo1);
+ assertFalse(DomUtilities.isEquivalent(root1, root2));
+ root2.appendChild(foo2);
+ assertTrue(DomUtilities.isEquivalent(root1, root2));
+
+ root1.appendChild(document1.createElement("bar"));
+ assertFalse(DomUtilities.isEquivalent(root1, root2));
+ root2.appendChild(document2.createElement("bar"));
+ assertTrue(DomUtilities.isEquivalent(root1, root2));
+
+ // Add attributes in opposite order
+ foo1.setAttribute("attribute1", "value1");
+ foo1.setAttribute("attribute2", "value2");
+ assertFalse(DomUtilities.isEquivalent(root1, root2));
+ foo2.setAttribute("attribute2", "value2");
+ foo2.setAttribute("attribute1", "valueWrong");
+ assertFalse(DomUtilities.isEquivalent(root1, root2));
+ foo2.setAttribute("attribute1", "value1");
+ assertTrue(DomUtilities.isEquivalent(root1, root2));
+
+ // TODO - test different tag names
+ // TODO - test different name spaces!
+ }
+
+ public void testIsContiguous() throws Exception {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.newDocument();
+ document.appendChild(document.createElement("root"));
+ Element root = document.getDocumentElement();
+ root.appendChild(document.createTextNode(" "));
+ Element foo = document.createElement("foo");
+ root.appendChild(foo);
+ root.appendChild(document.createTextNode(" "));
+ Element bar = document.createElement("bar");
+ root.appendChild(bar);
+ Element baz = document.createElement("baz");
+ root.appendChild(baz);
+
+ assertTrue(DomUtilities.isContiguous(Arrays.asList(foo)));
+ assertTrue(DomUtilities.isContiguous(Arrays.asList(foo, bar)));
+ assertTrue(DomUtilities.isContiguous(Arrays.asList(foo, bar, baz)));
+ assertTrue(DomUtilities.isContiguous(Arrays.asList(foo, bar, baz)));
+ assertTrue(DomUtilities.isContiguous(Arrays.asList(bar, baz, foo)));
+ assertTrue(DomUtilities.isContiguous(Arrays.asList(baz, bar, foo)));
+ assertTrue(DomUtilities.isContiguous(Arrays.asList(baz, foo, bar)));
+
+ assertFalse(DomUtilities.isContiguous(Arrays.asList(foo, baz)));
+ assertFalse(DomUtilities.isContiguous(Arrays.asList(root, baz)));
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PointTestCases.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PointTestCases.java
index 5e86d69..91d0e13 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PointTestCases.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PointTestCases.java
@@ -29,7 +29,7 @@ import junit.framework.TestCase;
* Common utilities for the point tests {@link LayoutPointTest} and
* {@link ControlPointTest}
*/
-public abstract class PointTestCases extends TestCase {
+public class PointTestCases extends TestCase {
LayoutCanvas mCanvas = new TestLayoutCanvas();
protected MouseEvent canvasMouseEvent(int x, int y, int stateMask) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java
new file mode 100644
index 0000000..b29f9f3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2011 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.editors.layout.gle2;
+
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+
+import org.eclipse.swt.widgets.Shell;
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+public class SelectionManagerTest extends TestCase {
+ private SelectionManager createManager() {
+ LayoutCanvas canvas = new LayoutCanvas(null, null, new Shell(), 0);
+ return new SelectionManager(canvas);
+ }
+
+ public void testEmpty() {
+ SelectionManager manager = createManager();
+
+ assertNotNull(manager.getSelections());
+ assertEquals(0, manager.getSelections().size());
+ assertFalse(manager.hasMultiSelection());
+ assertTrue(manager.isEmpty());
+ }
+
+ public void testBasic() {
+ SelectionManager manager = createManager();
+ assertTrue(manager.isEmpty());
+
+ UiViewElementNode rootNode = CanvasViewInfoTest.createNode("android.widget.LinearLayout",
+ true);
+ ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100);
+ UiViewElementNode child1Node = CanvasViewInfoTest.createNode(rootNode,
+ "android.widget.Button", false);
+ ViewInfo child1 = new ViewInfo("Button1", child1Node, 0, 0, 50, 20);
+ UiViewElementNode child2Node = CanvasViewInfoTest.createNode(rootNode,
+ "android.widget.Button", false);
+ ViewInfo child2 = new ViewInfo("Button2", child2Node, 0, 20, 70, 25);
+ root.setChildren(Arrays.asList(child1, child2));
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, true /* layoutlib5 */).getFirst();
+ assertNotNull(rootView);
+
+ manager.selectMultiple(Arrays.asList(rootView, rootView.getChildren().get(0), rootView
+ .getChildren().get(1)));
+ assertEquals(3, manager.getSelections().size());
+ assertFalse(manager.isEmpty());
+ assertTrue(manager.hasMultiSelection());
+
+ // Expect read-only result; ensure that's the case
+ try {
+ manager.getSelections().remove(0);
+ fail("Result should be read only collection");
+ } catch (Exception e) {
+ ; //ok, what we expected
+ }
+
+ manager.selectNone();
+ assertEquals(0, manager.getSelections().size());
+ assertTrue(manager.isEmpty());
+
+ manager.selectSingle(rootView);
+ assertEquals(1, manager.getSelections().size());
+ assertFalse(manager.isEmpty());
+ assertSame(rootView, manager.getSelections().get(0).getViewInfo());
+
+ manager.selectMultiple(Arrays.asList(rootView, rootView.getChildren().get(0), rootView
+ .getChildren().get(1)));
+ assertEquals(3, manager.getSelections().size());
+
+ manager.deselect(rootView.getChildren().get(0));
+ assertEquals(2, manager.getSelections().size());
+ manager.deselect(rootView);
+ assertEquals(1, manager.getSelections().size());
+ assertSame(rootView.getChildren().get(1), manager.getSelections().get(0).getViewInfo());
+ }
+
+ public void testSelectParent() {
+ SelectionManager manager = createManager();
+ assertTrue(manager.isEmpty());
+
+ UiViewElementNode rootNode = CanvasViewInfoTest.createNode("android.widget.LinearLayout",
+ true);
+ ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100);
+ UiViewElementNode child1Node = CanvasViewInfoTest.createNode(rootNode,
+ "android.widget.Button", false);
+ ViewInfo child1 = new ViewInfo("Button1", child1Node, 0, 0, 50, 20);
+ UiViewElementNode child2Node = CanvasViewInfoTest.createNode(rootNode,
+ "android.widget.Button", false);
+ ViewInfo child2 = new ViewInfo("Button2", child2Node, 0, 20, 70, 25);
+ root.setChildren(Arrays.asList(child1, child2));
+ CanvasViewInfo rootView = CanvasViewInfo.create(root, true /* layoutlib5 */).getFirst();
+ assertNotNull(rootView);
+
+ manager.selectMultiple(Arrays.asList(rootView.getChildren().get(0)));
+ assertEquals(1, manager.getSelections().size());
+ assertFalse(manager.isEmpty());
+ assertSame(rootView.getChildren().get(0), manager.getSelections().get(0).getViewInfo());
+
+ manager.selectParent();
+ assertEquals(1, manager.getSelections().size());
+ assertFalse(manager.isEmpty());
+ assertSame(rootView, manager.getSelections().get(0).getViewInfo());
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java
index 5561e52..de7999c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java
@@ -240,18 +240,27 @@ public class SwtUtilsTest extends TestCase {
scale, alpha);
assertNotNull(result);
- ImageData data = result.getImageData();
- byte[] alphaData = data.alphaData;
- assertNotNull(alphaData);
- assertEquals(20, data.width);
- assertEquals(20, data.height);
+ ImageData outData = result.getImageData();
+ assertEquals(20, outData.width);
+ assertEquals(20, outData.height);
+
+ PaletteData outPalette = outData.palette;
+ assertNotNull(outPalette);
+
+ byte[] outAlphaData = outData.alphaData;
+ assertNotNull(outAlphaData);
+
for (int y = 0; y < 20; y++) {
for (int x = 0; x < 20; x++) {
int r = y + 60;
int g = x + 30;
- int expected = r << 16 | g << 8;
- assertEquals(expected, data.getPixel(x, y));
- assertEquals(alpha, alphaData[y*20+x]);
+
+ RGB expected = new RGB(r, g, 0);
+ RGB actual = outPalette.getRGB(outData.getPixel(x, y));
+ assertEquals(expected, actual);
+
+ byte actualAlpha = outAlphaData[y*20+x];
+ assertEquals(alpha, actualAlpha);
}
}
}
@@ -270,30 +279,41 @@ public class SwtUtilsTest extends TestCase {
scale, alpha);
assertNotNull(result);
- ImageData data = result.getImageData();
- byte[] alphaData = data.alphaData;
- assertNotNull(alphaData);
- assertEquals(120, data.width);
- assertEquals(90, data.height);
+ ImageData outData = result.getImageData();
+ assertEquals(120, outData.width);
+ assertEquals(90, outData.height);
+
+ PaletteData outPalette = outData.palette;
+ assertNotNull(outPalette);
+
+ byte[] outAlphaData = outData.alphaData;
+ assertNotNull(outAlphaData);
+
for (int y = 0; y < 20; y++) {
for (int x = 0; x < 20; x++) {
int r = y + 10;
int g = x + 10;
- int expected = r << 16 | g << 8;
- assertEquals(expected, data.getPixel(x, y));
- assertEquals(alpha, alphaData[y*120+x]);
+
+ RGB expected = new RGB(r, g, 0);
+ RGB actual = outPalette.getRGB(outData.getPixel(x, y));
+ assertEquals(expected, actual);
+
+ assertEquals(alpha, outAlphaData[y*120+x]);
}
}
for (int y = 70; y < 90; y++) {
for (int x = 100; x < 120; x++) {
int r = y + 10;
int g = x + 10;
- int expected = r << 16 | g << 8;
- assertEquals(expected, data.getPixel(x, y));
- assertEquals(alpha, alphaData[y*120+x]);
+
+ RGB expected = new RGB(r, g, 0);
+ RGB actual = outPalette.getRGB(outData.getPixel(x, y));
+ assertEquals(expected, actual);
+
+ assertEquals(alpha, outAlphaData[y*120+x]);
}
}
- assertEquals(0, alphaData[40]);
+ assertEquals(0, outAlphaData[40]);
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java
index 277089f..9f670cc 100755
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java
@@ -48,7 +48,7 @@ public class NodeFactoryTest extends TestCase {
ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass");
UiViewElementNode uiv = new UiViewElementNode(ved);
ViewInfo lvi = new ViewInfo("name", uiv, 10, 12, 110, 120);
- CanvasViewInfo cvi = CanvasViewInfo.create(lvi).getFirst();
+ CanvasViewInfo cvi = CanvasViewInfo.create(lvi, true /* layoutlib5 */).getFirst();
// Create a NodeProxy.
NodeProxy proxy = m.create(cvi);
@@ -95,7 +95,7 @@ public class NodeFactoryTest extends TestCase {
ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass");
UiViewElementNode uiv = new UiViewElementNode(ved);
ViewInfo lvi = new ViewInfo("name", uiv, 10, 12, 110, 120);
- CanvasViewInfo cvi = CanvasViewInfo.create(lvi).getFirst();
+ CanvasViewInfo cvi = CanvasViewInfo.create(lvi, true /* layoutlib5 */).getFirst();
// NodeProxies are cached. Creating the same one twice returns the same proxy.
NodeProxy proxy1 = m.create(cvi);
@@ -107,7 +107,7 @@ public class NodeFactoryTest extends TestCase {
ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass");
UiViewElementNode uiv = new UiViewElementNode(ved);
ViewInfo lvi = new ViewInfo("name", uiv, 10, 12, 110, 120);
- CanvasViewInfo cvi = CanvasViewInfo.create(lvi).getFirst();
+ CanvasViewInfo cvi = CanvasViewInfo.create(lvi, true /* layoutlib5 */).getFirst();
// NodeProxies are cached. Creating the same one twice returns the same proxy.
NodeProxy proxy1 = m.create(cvi);
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java
index d18967d..5921e85 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gre;
import com.android.ide.common.api.IViewMetadata.FillPreference;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode;
import junit.framework.TestCase;
@@ -32,4 +33,31 @@ public class ViewMetadataRepositoryTest extends TestCase {
assertEquals(FillPreference.NONE,
repository.getFillPreference("foo.bar"));
}
+
+ // Ensure that all basenames referenced in the metadata refer to other views in the file
+ // (e.g. no typos)
+ public void testRelatedTo() throws Exception {
+ // Make sure unit tests are run with assertions on
+ boolean assertionsEnabled = false;
+ assert assertionsEnabled = true; // Intentional assignment
+ assertTrue("This unit test must be run with assertions enabled (-ea)", assertionsEnabled);
+
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+ for (String fqcn : repository.getAllFqcns()) {
+ repository.getRelatedTo(fqcn);
+ }
+ }
+
+ public void testSkip() throws Exception {
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+ assertTrue(repository.getSkip("merge"));
+ assertFalse(repository.getSkip("android.widget.Button"));
+ }
+
+ public void testRenderMode() throws Exception {
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+ assertEquals(RenderMode.NORMAL, repository.getRenderMode("android.widget.Button"));
+ assertEquals(RenderMode.SKIP, repository.getRenderMode("android.widget.LinearLayout"));
+ assertEquals(RenderMode.ALONE, repository.getRenderMode("android.widget.TabHost"));
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java
index 30f709c..ccf4e83 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java
@@ -16,15 +16,24 @@
package com.android.ide.eclipse.adt.internal.editors.manifest.model;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor.Mandatory;
import com.android.ide.eclipse.adt.internal.editors.mock.MockXmlNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.util.Iterator;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
import junit.framework.TestCase;
public class UiElementNodeTest extends TestCase {
@@ -252,4 +261,30 @@ public class UiElementNodeTest extends TestCase {
}
+ public void testlookupNamespacePrefix() throws Exception {
+ // Setup
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.newDocument();
+ Element rootElement = document.createElement("root");
+ Attr attr = document.createAttributeNS(XmlnsAttributeDescriptor.XMLNS_URI,
+ "xmlns:customPrefix");
+ attr.setValue(ANDROID_URI);
+ rootElement.getAttributes().setNamedItemNS(attr);
+ document.appendChild(rootElement);
+ Element root = document.getDocumentElement();
+ root.appendChild(document.createTextNode(" "));
+ Element foo = document.createElement("foo");
+ root.appendChild(foo);
+ root.appendChild(document.createTextNode(" "));
+ Element bar = document.createElement("bar");
+ root.appendChild(bar);
+ Element baz = document.createElement("baz");
+ root.appendChild(baz);
+
+ String prefix = UiElementNode.lookupNamespacePrefix(baz, ANDROID_URI);
+ assertEquals("customPrefix", prefix);
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java
index f8dc9fb..f2a6b54 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java
@@ -16,14 +16,12 @@
package com.android.ide.eclipse.adt.internal.editors.resources.manager;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
+import com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceFolder;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.common.resources.SingleResourceFile;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-import com.android.ide.eclipse.adt.internal.resources.manager.SingleResourceFile;
import com.android.ide.eclipse.adt.io.IFileWrapper;
import com.android.ide.eclipse.adt.io.IFolderWrapper;
import com.android.ide.eclipse.mock.Mocks;
@@ -33,16 +31,13 @@ import com.android.resources.KeyboardState;
import com.android.resources.Navigation;
import com.android.resources.NavigationState;
import com.android.resources.NightMode;
+import com.android.resources.ResourceFolderType;
import com.android.resources.ScreenOrientation;
import com.android.resources.TouchScreen;
-import com.android.sdklib.io.IAbstractFolder;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
import junit.framework.TestCase;
public class ConfigMatchTest extends TestCase {
@@ -50,8 +45,8 @@ public class ConfigMatchTest extends TestCase {
private static final String MISC1_FILENAME = "foo.xml"; //$NON-NLS-1$
private static final String MISC2_FILENAME = "bar.xml"; //$NON-NLS-1$
+ private FolderConfiguration mDefaultConfig;
private ProjectResources mResources;
- private ResourceQualifier[] mQualifierList;
private FolderConfiguration config4;
private FolderConfiguration config3;
private FolderConfiguration config2;
@@ -61,15 +56,9 @@ public class ConfigMatchTest extends TestCase {
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.
- 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 a default config with all qualifiers.
+ mDefaultConfig = new FolderConfiguration();
+ mDefaultConfig.createDefault();
// create the project resources.
mResources = new ProjectResources(null /*project*/);
@@ -233,17 +222,18 @@ public class ConfigMatchTest extends TestCase {
* this particular qualifier.
*/
private FolderConfiguration getConfiguration(String... qualifierValues) {
- FolderConfiguration config = new FolderConfiguration();
+ // FolderConfiguration.getQualifierCount is always valid and up to date.
+ final int count = FolderConfiguration.getQualifierCount();
- // those must be of the same length
- assertEquals(qualifierValues.length, mQualifierList.length);
+ // Check we have the right number of qualifier.
+ assertEquals(qualifierValues.length, count);
- int index = 0;
+ FolderConfiguration config = new FolderConfiguration();
- for (ResourceQualifier qualifier : mQualifierList) {
- String value = qualifierValues[index++];
+ for (int i = 0 ; i < count ; i++) {
+ String value = qualifierValues[i];
if (value != null) {
- assertTrue(qualifier.checkAndSet(value, config));
+ assertTrue(mDefaultConfig.getQualifier(i).checkAndSet(value, config));
}
}
@@ -253,11 +243,11 @@ public class ConfigMatchTest extends TestCase {
/**
* Adds a folder to the given {@link ProjectResources} with the given
* {@link FolderConfiguration}. The folder is filled with files from the provided list.
- * @param resources the {@link ProjectResources} in which to add the folder.
+ * @param resources the {@link ResourceRepository} in which to add the folder.
* @param config the {@link FolderConfiguration} for the created folder.
* @param memberList the list of files for the folder.
*/
- private void addFolder(ProjectResources resources, FolderConfiguration config,
+ private void addFolder(ResourceRepository resources, FolderConfiguration config,
IFile[] memberList) throws Exception {
// figure out the folder name based on the configuration
@@ -267,28 +257,11 @@ public class ConfigMatchTest extends TestCase {
IFolder folder = Mocks.createFolder(folderName, memberList);
// add it to the resource, and get back a ResourceFolder object.
- ResourceFolder resFolder = _addProjectResourceFolder(resources, config, folder);
+ ResourceFolder resFolder = resources.processFolder(new IFolderWrapper(folder));
// and fill it with files from the list.
for (IFile file : memberList) {
resFolder.addFile(new SingleResourceFile(new IFileWrapper(file), resFolder));
}
}
-
- /** 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, IFolder folder) throws Exception {
-
- Method addMethod = ProjectResources.class.getDeclaredMethod("add",
- ResourceFolderType.class, FolderConfiguration.class,
- IAbstractFolder.class);
- addMethod.setAccessible(true);
- ResourceFolder resFolder = (ResourceFolder)addMethod.invoke(resources,
- ResourceFolderType.LAYOUT, config, new IFolderWrapper(folder));
- return resFolder;
- }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/QualifierListTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/QualifierListTest.java
deleted file mode 100644
index 8932a00..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/QualifierListTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.internal.editors.resources.manager;
-
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-
-import java.lang.reflect.Field;
-
-import junit.framework.TestCase;
-
-public class QualifierListTest extends TestCase {
-
- private ResourceManager mManager;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
-
- mManager = ResourceManager.getInstance();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- mManager = null;
- }
-
- public void testQualifierList() {
- try {
- // get the list of qualifier in the resource manager
- Field qualifierListField = ResourceManager.class.getDeclaredField("mQualifiers");
- assertNotNull(qualifierListField);
- qualifierListField.setAccessible(true);
-
- // get the actual list.
- ResourceQualifier[] qualifierList =
- (ResourceQualifier[])qualifierListField.get(mManager);
-
- // now get the number of qualifier in the FolderConfiguration
- Field qualCountField = FolderConfiguration.class.getDeclaredField("INDEX_COUNT");
- assertNotNull(qualCountField);
- qualCountField.setAccessible(true);
-
- // get the constant value
- Integer count = (Integer)qualCountField.get(null);
-
- // now compare
- assertEquals(count.intValue(), qualifierList.length);
- } catch (SecurityException e) {
- assertTrue(false);
- } catch (NoSuchFieldException e) {
- assertTrue(false);
- } catch (IllegalArgumentException e) {
- assertTrue(false);
- } catch (IllegalAccessException e) {
- assertTrue(false);
- }
- }
-}
-
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/xml/HyperlinksTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/xml/HyperlinksTest.java
deleted file mode 100644
index ec6f99a..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/xml/HyperlinksTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2010 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.editors.xml;
-
-import junit.framework.TestCase;
-
-public class HyperlinksTest extends TestCase {
- public void testFqnRegexp() throws Exception {
- assertTrue(Hyperlinks.CLASS_PATTERN.matcher("com.android.Foo").matches());
- assertTrue(Hyperlinks.CLASS_PATTERN.matcher("com.android.pk_g.Foo_Bar1").
- matches());
- assertTrue(Hyperlinks.CLASS_PATTERN.matcher("com.android.Foo$Inner").matches());
-
- // Should we allow non-standard packages and class names?
- // For now, we're allowing it -- see how this works out in practice.
- //assertFalse(XmlHyperlinkResolver.CLASS_PATTERN.matcher("Foo.bar").matches());
- assertTrue(Hyperlinks.CLASS_PATTERN.matcher("Foo.bar").matches());
-
- assertFalse(Hyperlinks.CLASS_PATTERN.matcher("LinearLayout").matches());
- assertFalse(Hyperlinks.CLASS_PATTERN.matcher(".").matches());
- assertFalse(Hyperlinks.CLASS_PATTERN.matcher(".F").matches());
- assertFalse(Hyperlinks.CLASS_PATTERN.matcher("f.").matches());
- assertFalse(Hyperlinks.CLASS_PATTERN.matcher("Foo").matches());
- assertFalse(Hyperlinks.CLASS_PATTERN.matcher("com.android.1Foo").matches());
- assertFalse(Hyperlinks.CLASS_PATTERN.matcher("1com.Foo").matches());
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java
new file mode 100644
index 0000000..078e7cb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2011 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;
+
+import static com.android.resources.ResourceType.DIMEN;
+import static com.android.resources.ResourceType.LAYOUT;
+
+import com.android.ide.common.resources.ResourceDeltaKind;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.resources.ResourceType;
+
+import org.eclipse.core.resources.IResourceDelta;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test ResourceHelper
+ */
+public class ResourceHelperTest extends TestCase {
+
+ /**
+ * temp fake qualifier class.
+ */
+ private static class FakeQualifierClass extends ResourceQualifier {
+
+ @Override
+ public boolean checkAndSet(String value, FolderConfiguration config) {
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ return false;
+ }
+
+ @Override
+ public String getFolderSegment() {
+ return null;
+ }
+
+ @Override
+ public String getLongDisplayValue() {
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ public String getShortDisplayValue() {
+ return null;
+ }
+
+ @Override
+ public String getShortName() {
+ return null;
+ }
+
+ @Override
+ public boolean hasFakeValue() {
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public boolean isValid() {
+ return false;
+ }
+
+ }
+
+ public void testgetIcon() throws Exception {
+ // check that the method returns null for an unknown qualifier class
+ assertNull(ResourceHelper.getIcon(FakeQualifierClass.class));
+
+ // find all the qualifiers through FolderConfiguration.createdefault()
+ FolderConfiguration config = new FolderConfiguration();
+ config.createDefault();
+ final int count = FolderConfiguration.getQualifierCount();
+ for (int i = 0 ; i < count ; i++) {
+ ResourceQualifier qual = config.getQualifier(i);
+ assertNotNull(qual);
+ assertNotNull(qual.getClass().getCanonicalName(),
+ ResourceHelper.getIcon(qual.getClass()));
+ }
+ }
+
+ public void testGetResourceDeltaKind() {
+ assertEquals(ResourceDeltaKind.ADDED,
+ ResourceHelper.getResourceDeltaKind(IResourceDelta.ADDED));
+ assertEquals(ResourceDeltaKind.REMOVED,
+ ResourceHelper.getResourceDeltaKind(IResourceDelta.REMOVED));
+ assertEquals(ResourceDeltaKind.CHANGED,
+ ResourceHelper.getResourceDeltaKind(IResourceDelta.CHANGED));
+
+ assertNull(ResourceHelper.getResourceDeltaKind(IResourceDelta.ADDED_PHANTOM));
+ }
+
+ public void testParseResource() {
+ assertNull(ResourceHelper.parseResource(""));
+ assertNull(ResourceHelper.parseResource("not_a_resource"));
+
+ assertEquals(LAYOUT, ResourceHelper.parseResource("@layout/foo").getFirst());
+ assertEquals(DIMEN, ResourceHelper.parseResource("@dimen/foo").getFirst());
+ assertEquals(DIMEN, ResourceHelper.parseResource("@android:dimen/foo").getFirst());
+ assertEquals("foo", ResourceHelper.parseResource("@layout/foo").getSecond());
+ assertEquals("foo", ResourceHelper.parseResource("@dimen/foo").getSecond());
+ assertEquals("foo", ResourceHelper.parseResource("@android:dimen/foo").getSecond());
+ }
+
+
+ public void testIsFileBasedResourceType() throws Exception {
+ assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.ANIMATOR));
+ assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.LAYOUT));
+
+ assertFalse(ResourceHelper.isFileBasedResourceType(ResourceType.STRING));
+ assertFalse(ResourceHelper.isFileBasedResourceType(ResourceType.DIMEN));
+ assertFalse(ResourceHelper.isFileBasedResourceType(ResourceType.ID));
+
+ // Both:
+ assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.DRAWABLE));
+ assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.COLOR));
+ }
+
+ public void testIsValueBasedResourceType() throws Exception {
+ assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.STRING));
+ assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.DIMEN));
+ assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.ID));
+
+ assertFalse(ResourceHelper.isValueBasedResourceType(ResourceType.LAYOUT));
+
+ // These can be both:
+ assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.DRAWABLE));
+ assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.COLOR));
+ }
+
+ public void testCanCreateResource() throws Exception {
+ assertTrue(ResourceHelper.canCreateResource("@layout/foo"));
+ assertTrue(ResourceHelper.canCreateResource("@string/foo"));
+ assertTrue(ResourceHelper.canCreateResource("@dimen/foo"));
+ assertTrue(ResourceHelper.canCreateResource("@color/foo"));
+
+ assertFalse(ResourceHelper.canCreateResource("@typo/foo")); // nonexistent type
+ assertFalse(ResourceHelper.canCreateResource("@layout/foo bar")); // space
+ assertFalse(ResourceHelper.canCreateResource("@layout/new")); // keyword
+ assertFalse(ResourceHelper.canCreateResource("@android:string/foo")); // framework
+ assertFalse(ResourceHelper.canCreateResource("@android:dimen/foo"));
+ assertFalse(ResourceHelper.canCreateResource("@android:color/foo"));
+ }
+
+ public void testStyleToTheme() throws Exception {
+ assertEquals("Foo", ResourceHelper.styleToTheme("Foo"));
+ assertEquals("Theme", ResourceHelper.styleToTheme("@android:style/Theme"));
+ assertEquals("LocalTheme", ResourceHelper.styleToTheme("@style/LocalTheme"));
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java
new file mode 100644
index 0000000..b771667
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 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;
+
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+
+import java.util.Collections;
+
+import junit.framework.TestCase;
+
+public class ResourceNameValidatorTest extends TestCase {
+ public void testValidator() throws Exception {
+ // Valid
+ ResourceNameValidator validator = ResourceNameValidator.create(true,
+ ResourceFolderType.VALUES);
+ assertTrue(validator.isValid("foo") == null);
+ assertTrue(validator.isValid("foo.xml") == null);
+ assertTrue(validator.isValid("Foo123_$") == null);
+
+ // Invalid
+ assertTrue(validator.isValid("") != null);
+ assertTrue(validator.isValid(" ") != null);
+ assertTrue(validator.isValid("foo.xm") != null);
+ assertTrue(validator.isValid("foo bar") != null);
+ assertTrue(validator.isValid("1foo") != null);
+ assertTrue(validator.isValid("foo%bar") != null);
+ assertTrue(ResourceNameValidator.create(true, Collections.singleton("foo"),
+ ResourceType.STRING).isValid("foo") != null);
+
+ // Only lowercase chars allowed in file-based resource names
+ assertTrue(ResourceNameValidator.create(true, ResourceFolderType.LAYOUT)
+ .isValid("Foo123_$") != null);
+ assertTrue(ResourceNameValidator.create(true, ResourceFolderType.LAYOUT)
+ .isValid("foo123_") == null);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ResourceNameValidatorTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ResourceNameValidatorTest.java
deleted file mode 100644
index 5ee6793..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ResourceNameValidatorTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ide.eclipse.adt.internal.wizards.newxmlfile;
-
-import java.util.Collections;
-
-import junit.framework.TestCase;
-
-public class ResourceNameValidatorTest extends TestCase {
- public void testValidator() throws Exception {
- // Valid
- assertTrue(ResourceNameValidator.create(true).isValid("foo") == null);
- assertTrue(ResourceNameValidator.create(true).isValid("foo.xml") == null);
- assertTrue(ResourceNameValidator.create(true).isValid("Foo123_$") == null);
-
- // Invalid
- assertTrue(ResourceNameValidator.create(true).isValid("") != null);
- assertTrue(ResourceNameValidator.create(true).isValid(" ") != null);
- assertTrue(ResourceNameValidator.create(true).isValid("foo.xm") != null);
- assertTrue(ResourceNameValidator.create(true).isValid("foo bar") != null);
- assertTrue(ResourceNameValidator.create(true).isValid("1foo") != null);
- assertTrue(ResourceNameValidator.create(true).isValid("foo%bar") != null);
- assertTrue(ResourceNameValidator.create(true, Collections.singleton("foo"))
- .isValid("foo") != null);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.traceview/.settings/org.eclipse.jdt.core.prefs b/eclipse/plugins/com.android.ide.eclipse.traceview/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.traceview/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/eclipse/plugins/com.android.ide.eclipse.traceview/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.traceview/META-INF/MANIFEST.MF
index ab9a5c2..35c62e5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.traceview/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.traceview/META-INF/MANIFEST.MF
@@ -2,12 +2,12 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Traceview
Bundle-SymbolicName: com.android.ide.eclipse.traceview;singleton:=true
-Bundle-Version: 10.0.0.qualifier
+Bundle-Version: 11.0.0.qualifier
Bundle-Activator: com.android.ide.eclipse.traceview.TraceviewPlugin
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.ui.ide,
- com.android.ide.eclipse.ddms;bundle-version="10.0.0",
+ com.android.ide.eclipse.ddms;bundle-version="11.0.0",
org.eclipse.core.filesystem,
org.eclipse.core.resources,
org.eclipse.jdt.core,
diff --git a/eclipse/plugins/com.android.ide.eclipse.traceview/about.ini b/eclipse/plugins/com.android.ide.eclipse.traceview/about.ini
new file mode 100644
index 0000000..b61f646
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.traceview/about.ini
@@ -0,0 +1,2 @@
+aboutText=%blurb
+featureImage=icons/traceview-32.png \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.traceview/about.properties b/eclipse/plugins/com.android.ide.eclipse.traceview/about.properties
new file mode 100755
index 0000000..b33e9f3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.traceview/about.properties
@@ -0,0 +1,7 @@
+blurb=Traceview\n\
+\n\
+Version\: {featureVersion}\n\
+\n\
+(c) Copyright 2011 The Android Open Source Project. All rights reserved.\n\
+Visit http://developer.android.com/sdk/eclipse-adt.html
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.traceview/build.properties b/eclipse/plugins/com.android.ide.eclipse.traceview/build.properties
index 049e0d1..f529710 100644
--- a/eclipse/plugins/com.android.ide.eclipse.traceview/build.properties
+++ b/eclipse/plugins/com.android.ide.eclipse.traceview/build.properties
@@ -4,4 +4,6 @@ bin.includes = META-INF/,\
.,\
plugin.xml,\
libs/traceview.jar,\
- icons/
+ icons/,\
+ about.ini,\
+ about.properties
diff --git a/eclipse/plugins/com.android.ide.eclipse.traceview/icons/traceview-32.png b/eclipse/plugins/com.android.ide.eclipse.traceview/icons/traceview-32.png
new file mode 100644
index 0000000..4916737
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.traceview/icons/traceview-32.png
Binary files differ
diff --git a/eclipse/scripts/create_all_symlinks.sh b/eclipse/scripts/create_all_symlinks.sh
index dec4df7..f2ce44c 100755
--- a/eclipse/scripts/create_all_symlinks.sh
+++ b/eclipse/scripts/create_all_symlinks.sh
@@ -27,5 +27,7 @@ echo ; echo "### HIERARCHYVIEWER ###" ; echo
$DEST/create_hierarchyviewer_symlinks.sh "$*"
echo ; echo "### TRACEVIEW ###" ; echo
$DEST/create_traceview_symlinks.sh "$*"
+echo ; echo "### SDKMANAGER ###" ; echo
+$DEST/create_sdkman_symlinks.sh "$*"
echo "### $0 done"
diff --git a/eclipse/scripts/create_ddms_symlinks.sh b/eclipse/scripts/create_ddms_symlinks.sh
index 8791316..48f5c51 100755
--- a/eclipse/scripts/create_ddms_symlinks.sh
+++ b/eclipse/scripts/create_ddms_symlinks.sh
@@ -63,11 +63,12 @@ for i in prebuilt/common/jfreechart/*.jar; do
cpfile $DEST $i
done
-LIBS="ddmlib ddmuilib"
+COPY_LIBS="ddmlib ddmuilib"
+ALL_LIBS="$COPY_LIBS swtmenubar"
echo "make java libs ..."
-make -j3 showcommands $LIBS || die "DDMS: Fail to build one of $LIBS."
+make -j3 showcommands $ALL_LIBS || die "DDMS: Fail to build one of $ALL_LIBS."
-for LIB in $LIBS; do
+for LIB in $COPY_LIBS; do
cpfile $DEST out/host/$PLATFORM/framework/$LIB.jar
done
diff --git a/eclipse/scripts/create_hierarchyviewer_symlinks.sh b/eclipse/scripts/create_hierarchyviewer_symlinks.sh
index e0439ef..47dbe7f 100755
--- a/eclipse/scripts/create_hierarchyviewer_symlinks.sh
+++ b/eclipse/scripts/create_hierarchyviewer_symlinks.sh
@@ -60,10 +60,11 @@ DEST=$BASE/libs
mkdir -p $DEST
-LIBS="hierarchyviewerlib "
+COPY_LIBS="hierarchyviewerlib"
+ALL_LIBS="$COPY_LIBS swtmenubar"
echo "make java libs ..."
-make -j3 showcommands $LIBS || die "Hierarchy Viewer: Fail to build one of $LIBS."
+make -j3 showcommands $ALL_LIBS || die "Hierarchy Viewer: Fail to build one of $ALL_LIBS."
-for LIB in $LIBS; do
+for LIB in $COPY_LIBS; do
cpfile $DEST out/host/$PLATFORM/framework/$LIB.jar
done
diff --git a/eclipse/scripts/create_sdkman_symlinks.sh b/eclipse/scripts/create_sdkman_symlinks.sh
new file mode 100755
index 0000000..d965195
--- /dev/null
+++ b/eclipse/scripts/create_sdkman_symlinks.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+function die() {
+ echo "Error: $*"
+ exit 1
+}
+
+set -e # fail early
+
+# CD to the top android directory
+D=`dirname "$0"`
+cd "$D/../../../"
+
+LIBS="swtmenubar"
+
+echo "SDK Manager: make java libs $LIBS"
+make -j3 showcommands $LIBS || die "SDK Manager: Failed to build one of $LIBS."
diff --git a/eclipse/scripts/update_version.sh b/eclipse/scripts/update_version.sh
index a288965..c7198af 100644..100755
--- a/eclipse/scripts/update_version.sh
+++ b/eclipse/scripts/update_version.sh
@@ -22,16 +22,10 @@ if [ `basename "$PWD"` != "eclipse" ]; then
fi
# quote dots for regexps
-OLD="${OLD//./\.}"
-NEW="${NEW//./\.}"
-
-# Find all the files with the old pattern, except changes.txt and
-# p4 edit them. Skip that if there's no p4 in path.
-if which g4 1>/dev/null 2>/dev/null ; then
- grep -rl "$OLD" * | grep -E "\.xml$|\.MF$" | xargs -n 5 g4 edit
-fi
+OLD="${OLD//./\.}\.qualifier"
+NEW="${NEW//./\.}\.qualifier"
# Now find the same files but this time use sed to replace in-place with
# the new pattern. Old files get backuped with the .old extension.
-grep -rl "$OLD" * | grep -E "\.xml$|\.MF$" | xargs -n 1 sed -i.old "s/$OLD/$NEW/g"
+grep -rl "$OLD" * | grep -E "\.xml$|\.MF$" | xargs -n 1 sed -i -e "s/$OLD/$NEW/g"
diff --git a/eclipse/sites/external/site.xml b/eclipse/sites/external/site.xml
index f4810c8..bd3a044 100644
--- a/eclipse/sites/external/site.xml
+++ b/eclipse/sites/external/site.xml
@@ -3,16 +3,16 @@
<description url="https://dl-ssl.google.com/android/eclipse/">
Update Site for Android Development Toolkit
</description>
- <feature url="features/com.android.ide.eclipse.adt_10.0.0.qualifier.jar" id="com.android.ide.eclipse.adt" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.adt_11.0.0.qualifier.jar" id="com.android.ide.eclipse.adt" version="11.0.0.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.ddms_10.0.0.qualifier.jar" id="com.android.ide.eclipse.ddms" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.ddms_11.0.0.qualifier.jar" id="com.android.ide.eclipse.ddms" version="11.0.0.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.hierarchyviewer_10.0.0.qualifier.jar" id="com.android.ide.eclipse.hierarchyviewer" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.hierarchyviewer_11.0.0.qualifier.jar" id="com.android.ide.eclipse.hierarchyviewer" version="11.0.0.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.traceview_10.0.0.qualifier.jar" id="com.android.ide.eclipse.traceview" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.traceview_11.0.0.qualifier.jar" id="com.android.ide.eclipse.traceview" version="11.0.0.qualifier">
<category name="developer"/>
</feature>
<category-def name="developer" label="Developer Tools">
diff --git a/eclipse/sites/internal/.gitignore b/eclipse/sites/internal/.gitignore
new file mode 100644
index 0000000..ccfee1d
--- /dev/null
+++ b/eclipse/sites/internal/.gitignore
@@ -0,0 +1,2 @@
+*.jar
+*/*.jar \ No newline at end of file
diff --git a/eclipse/sites/internal/site.xml b/eclipse/sites/internal/site.xml
index 40abd3b..02ba7fe 100644
--- a/eclipse/sites/internal/site.xml
+++ b/eclipse/sites/internal/site.xml
@@ -3,24 +3,24 @@
<description url="https://android.corp.google.com/adt/">
Update Site for Android Development Toolkit
</description>
- <feature url="features/com.android.ide.eclipse.adt_10.0.0.qualifier.jar" id="com.android.ide.eclipse.adt" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.adt_11.0.0.qualifier.jar" id="com.android.ide.eclipse.adt" version="11.0.0.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.ddms_10.0.0.qualifier.jar" id="com.android.ide.eclipse.ddms" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.ddms_11.0.0.qualifier.jar" id="com.android.ide.eclipse.ddms" version="11.0.0.qualifier">
<category name="developer"/>
<category name="platform"/>
</feature>
- <feature url="features/com.android.ide.eclipse.hierarchyviewer_10.0.0.qualifier.jar" id="com.android.ide.eclipse.hierarchyviewer" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.hierarchyviewer_11.0.0.qualifier.jar" id="com.android.ide.eclipse.hierarchyviewer" version="11.0.0.qualifier">
<category name="developer"/>
<category name="platform"/>
</feature>
- <feature url="features/com.android.ide.eclipse.tests_10.0.0.qualifier.jar" id="com.android.ide.eclipse.tests" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.tests_11.0.0.qualifier.jar" id="com.android.ide.eclipse.tests" version="11.0.0.qualifier">
<category name="test"/>
</feature>
- <feature url="features/com.android.ide.eclipse.pdt_10.0.0.qualifier.jar" id="com.android.ide.eclipse.pdt" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.pdt_11.0.0.qualifier.jar" id="com.android.ide.eclipse.pdt" version="11.0.0.qualifier">
<category name="platform"/>
</feature>
- <feature url="features/com.android.ide.eclipse.traceview_10.0.0.qualifier.jar" id="com.android.ide.eclipse.traceview" version="10.0.0.qualifier">
+ <feature url="features/com.android.ide.eclipse.traceview_11.0.0.qualifier.jar" id="com.android.ide.eclipse.traceview" version="11.0.0.qualifier">
<category name="developer"/>
<category name="platform"/>
</feature>
diff --git a/emulator/mksdcard/mksdcard.c b/emulator/mksdcard/mksdcard.c
index c85d0f8..c9a3eeb 100644
--- a/emulator/mksdcard/mksdcard.c
+++ b/emulator/mksdcard/mksdcard.c
@@ -27,7 +27,7 @@
/* a simple and portable program used to generate a blank FAT32 image file
*
- * usage: mksdcard [-l label] <size> <filename>
+ * usage: mksdcard [-l label] <size> <filename>
*/
#include <time.h>
@@ -43,6 +43,10 @@
#define BACKUP_BOOT_SECTOR 6
#define NUM_FATS 2
+/* sectors_per_disk is encoded as a signed int */
+#define MAX_SECTORS_PER_DISK 0x7FFFFFFF
+#define MAX_DISK_SIZE ((Wide)MAX_SECTORS_PER_DISK * BYTES_PER_SECTOR)
+
typedef long long Wide; /* might be something else if you don't use GCC */
typedef unsigned char Byte;
typedef Byte* Bytes;
@@ -53,10 +57,10 @@ typedef Byte* Bytes;
#define POKES(p,v) ( BYTE_(p,0) = (Byte)(v), BYTE_(p,1) = (Byte)((v) >> 8) )
#define POKEW(p,v) ( BYTE_(p,0) = (Byte)(v), BYTE_(p,1) = (Byte)((v) >> 8), BYTE_(p,2) = (Byte)((v) >> 16), BYTE_(p,3) = (Byte)((v) >> 24) )
-static Byte s_boot_sector [ BYTES_PER_SECTOR ]; /* boot sector */
+static Byte s_boot_sector [ BYTES_PER_SECTOR ]; /* boot sector */
static Byte s_fsinfo_sector [ BYTES_PER_SECTOR ]; /* FS Info sector */
-static Byte s_fat_head [ BYTES_PER_SECTOR ]; /* first FAT sector */
-static Byte s_zero_sector [ BYTES_PER_SECTOR ]; /* empty sector */
+static Byte s_fat_head [ BYTES_PER_SECTOR ]; /* first FAT sector */
+static Byte s_zero_sector [ BYTES_PER_SECTOR ]; /* empty sector */
/* this is the date and time when creating the disk */
static int
@@ -172,22 +176,28 @@ fat_init( Bytes fat )
static int
write_sector( FILE* file, Bytes sector )
{
- return fwrite( sector, 1, 512, file ) != 512;
+ int result = fwrite( sector, 1, BYTES_PER_SECTOR, file ) != BYTES_PER_SECTOR;
+ if (result) {
+ fprintf(stderr, "Failed to write sector of %d bytes: %s\n", BYTES_PER_SECTOR, strerror(errno));
+ }
+ return result;
}
static int
write_empty( FILE* file, Wide count )
{
- static Byte empty[64*1024];
+ static Byte empty[256*1024];
+ memset(empty, 0, sizeof(empty));
- count *= 512;
+ count *= BYTES_PER_SECTOR;
while (count > 0) {
int len = sizeof(empty);
if (len > count)
- len = count;
-
- if ( fwrite( empty, 1, len, file ) != (size_t)len )
+ len = count;
+ if ( fwrite( empty, 1, len, file ) != (size_t)len ) {
+ fprintf(stderr, "Failed to write %d bytes: %s\n", len, strerror(errno));
return 1;
+ }
count -= len;
}
@@ -201,6 +211,10 @@ static void usage (void)
fprintf(stderr, " if <size> is a simple integer, it specifies a size in bytes\n" );
fprintf(stderr, " if <size> is an integer followed by 'K', it specifies a size in KiB\n" );
fprintf(stderr, " if <size> is an integer followed by 'M', it specifies a size in MiB\n" );
+ fprintf(stderr, " if <size> is an integer followed by 'G', it specifies a size in GiB\n" );
+ fprintf(stderr, "\nMinimum size is 9M. The Android emulator cannot use smaller images.\n" );
+ fprintf(stderr, "Maximum size is %lld bytes, %lldK, %lldM or %lldG\n",
+ MAX_DISK_SIZE, MAX_DISK_SIZE >> 10, MAX_DISK_SIZE >> 20, MAX_DISK_SIZE >> 30);
exit(1);
}
@@ -211,7 +225,7 @@ int main( int argc, char** argv )
int sectors_per_disk;
char* end;
const char* label = NULL;
- FILE* f;
+ FILE* f = NULL;
for ( ; argc > 1 && argv[1][0] == '-'; argc--, argv++ )
{
@@ -238,19 +252,28 @@ int main( int argc, char** argv )
if (argc != 3)
usage();
- disk_size = strtol( argv[1], &end, 10 );
- if (disk_size == 0 && errno == EINVAL)
+ disk_size = strtoll( argv[1], &end, 10 );
+ if (disk_size <= 0 || errno == EINVAL || errno == ERANGE) {
+ fprintf(stderr, "Invalid argument size '%s'\n\n", argv[1]);
usage();
+ }
if (*end == 'K')
disk_size *= 1024;
else if (*end == 'M')
disk_size *= 1024*1024;
+ else if (*end == 'G')
+ disk_size *= 1024*1024*1024;
- if (disk_size < 8*1024*1024)
- fprintf(stderr, "### WARNING : SD Card images < 8 MB cannot be used with the Android emulator\n");
+ if (disk_size < 9*1024*1024) {
+ fprintf(stderr, "Invalid argument: size '%s' is too small.\n\n", argv[1]);
+ usage();
+ } else if (disk_size > MAX_DISK_SIZE) {
+ fprintf(stderr, "Invalid argument: size '%s' is too large.\n\n", argv[1]);
+ usage();
+ }
- sectors_per_disk = disk_size / 512;
+ sectors_per_disk = disk_size / BYTES_PER_SECTOR;
sectors_per_fat = get_sectors_per_fat( disk_size, get_sectors_per_cluster( disk_size ) );
boot_sector_init( s_boot_sector, s_fsinfo_sector, disk_size, NULL );
@@ -258,7 +281,8 @@ int main( int argc, char** argv )
f = fopen( argv[2], "wb" );
if ( !f ) {
- fprintf(stderr, "could not create file '%s', aborting...\n", argv[2] );
+ fprintf(stderr, "Could not create file '%s': %s\n", argv[2], strerror(errno));
+ goto FailWrite;
}
/* here's the layout:
@@ -274,7 +298,7 @@ int main( int argc, char** argv )
* zero sectors
*/
- if ( write_sector( f, s_boot_sector ) ) goto FailWrite;
+ if ( write_sector( f, s_boot_sector ) ) goto FailWrite;
if ( write_sector( f, s_fsinfo_sector ) ) goto FailWrite;
if ( BACKUP_BOOT_SECTOR > 0 ) {
if ( write_empty( f, BACKUP_BOOT_SECTOR - 2 ) ) goto FailWrite;
@@ -282,8 +306,7 @@ int main( int argc, char** argv )
if ( write_sector( f, s_fsinfo_sector ) ) goto FailWrite;
if ( write_empty( f, RESERVED_SECTORS - 2 - BACKUP_BOOT_SECTOR ) ) goto FailWrite;
}
- else
- if ( write_empty( f, RESERVED_SECTORS - 2 ) ) goto FailWrite;
+ else if ( write_empty( f, RESERVED_SECTORS - 2 ) ) goto FailWrite;
if ( write_sector( f, s_fat_head ) ) goto FailWrite;
if ( write_empty( f, sectors_per_fat-1 ) ) goto FailWrite;
@@ -297,8 +320,10 @@ int main( int argc, char** argv )
return 0;
FailWrite:
- fprintf(stderr, "could not write to '%s', aborting...\n", argv[2] );
- unlink( argv[2] );
- fclose(f);
+ if (f != NULL) {
+ fclose(f);
+ unlink( argv[2] );
+ fprintf(stderr, "File '%s' was not created.\n", argv[2]);
+ }
return 1;
}
diff --git a/emulator/qemud/qemud.c b/emulator/qemud/qemud.c
index e1c7b54..dc04de8 100644
--- a/emulator/qemud/qemud.c
+++ b/emulator/qemud/qemud.c
@@ -81,7 +81,7 @@
/* name of the single control socket used by the daemon */
#define CONTROL_SOCKET_NAME "qemud"
-#define DEBUG 1
+#define DEBUG 0
#define T_ACTIVE 0 /* set to 1 to dump traffic */
#if DEBUG
diff --git a/emulator/tests/Android.mk b/emulator/tests/Android.mk
new file mode 100644
index 0000000..04917f4
--- /dev/null
+++ b/emulator/tests/Android.mk
@@ -0,0 +1,17 @@
+# This directory contains various host tests to be used with the emulator
+# NOTE: Most of these are only built and run on Linux.
+
+LOCAL_PATH := $(call my-dir)
+
+# The test-qemud-pipes program is used to check the execution of QEMUD Pipes
+# See external/qemu/docs/ANDROID-QEMUD-PIPES.TXT for details.
+#
+ifeq ($(HOST_OS),XXXXlinux)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test-qemud-pipes
+LOCAL_SRC_FILES := test-qemud-pipes.c
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # HOST_OS == linux \ No newline at end of file
diff --git a/emulator/tests/test-qemud-pipes.c b/emulator/tests/test-qemud-pipes.c
new file mode 100644
index 0000000..f5db531
--- /dev/null
+++ b/emulator/tests/test-qemud-pipes.c
@@ -0,0 +1,113 @@
+/* This program is used to test the QEMUD fast pipes.
+ * See external/qemu/docs/ANDROID-QEMUD-PIPES.TXT for details.
+ *
+ * The program acts as a simple TCP server that accepts data and sends
+ * them back to the client.
+ */
+
+#include <sys/socket.h>
+#include <net/inet.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#define DEFAULT_PORT 8012
+
+static void
+socket_close(int sock)
+{
+ int old_errno = errno;
+ close(sock);
+ errno = old_errno;
+}
+
+static int
+socket_loopback_server( int port, int type )
+{
+ struct sockaddr_in addr;
+
+ int sock = socket(AF_INET, type, 0);
+ if (sock < 0) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ int n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if (TEMP_FAILURE_RETRY(bind(sock, &addr, sizeof(addr))) < 0) {
+ socket_close(sock);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ if (TEMP_FAILURE_RETRY(listen(sock, 4)) < 0) {
+ socket_close(sock);
+ return -1;
+ }
+ }
+
+ return sock;
+}
+
+int main(void)
+{
+ int sock, client;
+ int port = DEFAULT_PORT;
+
+ printf("Starting pipe test server on local port %d\n", port);
+ sock = socket_loopback_server( port, SOCK_STREAM );
+ if (sock < 0) {
+ fprintf(stderr, "Could not start server: %s\n", strerror(errno));
+ return 1;
+ }
+
+ client = accept(sock, NULL, NULL);
+ if (client < 0) {
+ fprintf(stderr, "Server error: %s\n", strerror(errno));
+ return 2;
+ }
+ printf("Client connected!\n");
+
+ /* Now, accept any incoming data, and send it back */
+ for (;;) {
+ char buff[1024], *p;
+ int ret, count;
+
+ do {
+ ret = read(client, buff, sizeof(buff));
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ fprintf(stderr, "Client read error: %s\n", strerror(errno));
+ close(client);
+ return 3;
+ }
+ count = ret;
+ p = buff;
+ printf(" received: %d bytes\n", count);
+
+ while (count > 0) {
+ do {
+ ret = write(client, p, count);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ fprintf(stderr, "Client write error: %s\n", strerror(errno));
+ close(client);
+ return 4;
+ }
+ printf(" sent: %d bytes\n", ret);
+
+ p += ret;
+ count -= ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/eventanalyzer/NOTICE b/eventanalyzer/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/eventanalyzer/NOTICE
@@ -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/files/devices.xml b/files/devices.xml
index 09171c9..ca26416 100644
--- a/files/devices.xml
+++ b/files/devices.xml
@@ -234,6 +234,33 @@
</d:config>
</d:device>
+ <d:device name="4in WVGA (Nexus S)">
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>hdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:nav-state>navexposed</d:nav-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>800</d:size>
+ </d:screen-dimension>
+ <d:xdpi>235</d:xdpi>
+ <d:ydpi>235</d:ydpi>
+ </d:default>
+
+ <d:config name="Portrait">
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape">
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ </d:device>
+
<d:device name="5.1in WVGA">
<d:default>
<d:screen-size>large</d:screen-size>
@@ -288,7 +315,7 @@
</d:config>
</d:device>
- <d:device name="10.1in WXGA">
+ <d:device name="10.1in WXGA (Tablet)">
<d:default>
<d:screen-size>xlarge</d:screen-size>
<d:screen-ratio>long</d:screen-ratio>
diff --git a/files/tools_source.properties b/files/tools_source.properties
index 68ec0ae..ca992d1 100644
--- a/files/tools_source.properties
+++ b/files/tools_source.properties
@@ -1,3 +1,3 @@
Pkg.UserSrc=false
-Pkg.Revision=10
+Pkg.Revision=11
Platform.MinPlatformToolsRev=3
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewManager.java b/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewManager.java
index df2a63e..ba346df 100644
--- a/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewManager.java
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewManager.java
@@ -35,6 +35,10 @@ public class ViewManager {
sendCommand("REQUEST_LAYOUT", device, window, params);
}
+ public static void outputDisplayList(IDevice device, Window window, String params) {
+ sendCommand("OUTPUT_DISPLAYLIST", device, window, params);
+ }
+
private static void sendCommand(String command, IDevice device, Window window, String params) {
Socket socket = null;
BufferedWriter out = null;
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java
index a7db985..82375e0 100644
--- a/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java
@@ -29,6 +29,7 @@ import com.android.hierarchyviewer.scene.ViewManager;
import com.android.hierarchyviewer.scene.ViewNode;
import com.android.hierarchyviewer.scene.WindowsLoader;
import com.android.hierarchyviewer.scene.ProfilesLoader;
+import com.android.hierarchyviewer.ui.action.DumpDisplayListAction;
import com.android.hierarchyviewer.ui.util.PsdFileFilter;
import com.android.hierarchyviewer.util.OS;
import com.android.hierarchyviewer.util.WorkerThread;
@@ -145,6 +146,7 @@ public class Workspace extends JFrame {
private JPanel mainPanel;
private JProgressBar progress;
private JToolBar buttonsPanel;
+ private JToolBar commandButtonsPanel;
private JComponent deviceSelector;
private DevicesTableModel devicesTableModel;
@@ -154,6 +156,7 @@ public class Workspace extends JFrame {
private Window currentWindow = Window.FOCUSED_WINDOW;
private JButton displayNodeButton;
+ private JButton dumpDisplayListButton;
private JButton captureLayersButton;
private JButton invalidateButton;
private JButton requestLayoutButton;
@@ -202,6 +205,7 @@ public class Workspace extends JFrame {
actionsMap.put(StopServerAction.ACTION_NAME, new StopServerAction(this));
actionsMap.put(InvalidateAction.ACTION_NAME, new InvalidateAction(this));
actionsMap.put(RequestLayoutAction.ACTION_NAME, new RequestLayoutAction(this));
+ actionsMap.put(DumpDisplayListAction.ACTION_NAME, new DumpDisplayListAction(this));
actionsMap.put(CaptureNodeAction.ACTION_NAME, new CaptureNodeAction(this));
actionsMap.put(CaptureLayersAction.ACTION_NAME, new CaptureLayersAction(this));
actionsMap.put(RefreshWindowsAction.ACTION_NAME, new RefreshWindowsAction(this));
@@ -210,11 +214,12 @@ public class Workspace extends JFrame {
private JComponent buildMainPanel() {
mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
- mainPanel.add(buildToolBar(), BorderLayout.PAGE_START);
+ commandButtonsPanel = buildToolBar();
+ mainPanel.add(commandButtonsPanel, BorderLayout.PAGE_START);
mainPanel.add(deviceSelector = buildDeviceSelector(), BorderLayout.CENTER);
mainPanel.add(buildStatusPanel(), BorderLayout.SOUTH);
- mainPanel.setPreferredSize(new Dimension(950, 800));
+ mainPanel.setPreferredSize(new Dimension(1200, 800));
return mainPanel;
}
@@ -481,6 +486,11 @@ public class Workspace extends JFrame {
displayNodeButton.putClientProperty("JButton.segmentPosition", "first");
toolBar.add(displayNodeButton);
+ dumpDisplayListButton = new JButton();
+ dumpDisplayListButton.setAction(actionsMap.get(DumpDisplayListAction.ACTION_NAME));
+ dumpDisplayListButton.putClientProperty("JButton.buttonType", "segmentedTextured");
+ dumpDisplayListButton.putClientProperty("JButton.segmentPosition", "middle");
+
captureLayersButton = new JButton();
captureLayersButton.setAction(actionsMap.get(CaptureLayersAction.ACTION_NAME));
captureLayersButton.putClientProperty("JButton.buttonType", "segmentedTextured");
@@ -502,6 +512,17 @@ public class Workspace extends JFrame {
return toolBar;
}
+ private void setupProtocolDependentToolbar() {
+ // Some functionality is only enabled in certain versions of the protocol.
+ // Add/remove those buttons here
+ if (protocolVersion < 4) {
+ commandButtonsPanel.remove(dumpDisplayListButton);
+ } else if (dumpDisplayListButton.getParent() == null) {
+ commandButtonsPanel.add(dumpDisplayListButton,
+ commandButtonsPanel.getComponentCount() - 1);
+ }
+ }
+
private JMenuBar buildMenuBar() {
JMenuBar menuBar = new JMenuBar();
@@ -885,6 +906,7 @@ public class Workspace extends JFrame {
displayNodeButton.setEnabled(false);
captureLayersButton.setEnabled(false);
invalidateButton.setEnabled(false);
+ dumpDisplayListButton.setEnabled(false);
requestLayoutButton.setEnabled(false);
graphViewButton.setEnabled(false);
pixelPerfectViewButton.setEnabled(false);
@@ -914,6 +936,7 @@ public class Workspace extends JFrame {
displayNodeButton.setEnabled(false);
captureLayersButton.setEnabled(false);
invalidateButton.setEnabled(false);
+ dumpDisplayListButton.setEnabled(false);
graphViewButton.setEnabled(false);
pixelPerfectViewButton.setEnabled(false);
requestLayoutButton.setEnabled(false);
@@ -1008,6 +1031,13 @@ public class Workspace extends JFrame {
return new CaptureNodeTask();
}
+ public SwingWorker<?, ?> outputDisplayList() {
+ if (scene.getFocusedObject() == null) {
+ return null;
+ }
+ return new DumpDisplayListTask();
+ }
+
public SwingWorker<?, ?> captureLayers() {
JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(new PsdFileFilter());
@@ -1081,6 +1111,27 @@ public class Workspace extends JFrame {
}
}
+ private class DumpDisplayListTask extends SwingWorker<Object, Void> {
+ private String captureParams;
+
+ private DumpDisplayListTask() {
+ captureParams = scene.getFocusedObject().toString();
+ beginTask();
+ }
+
+ @Override
+ @WorkerThread
+ protected Object doInBackground() throws Exception {
+ ViewManager.outputDisplayList(currentDevice, currentWindow, captureParams);
+ return null;
+ }
+
+ @Override
+ protected void done() {
+ endTask();
+ }
+ }
+
private class RequestLayoutTask extends SwingWorker<Object, Void> {
private String captureParams;
@@ -1182,7 +1233,7 @@ public class Workspace extends JFrame {
WindowsResult result = get();
protocolVersion = result.protocolVersion;
serverVersion = result.serverVersion;
-
+ setupProtocolDependentToolbar();
windowsTableModel.clear();
windowsTableModel.addWindows(result.windows);
} catch (ExecutionException e) {
@@ -1324,6 +1375,7 @@ public class Workspace extends JFrame {
public void focusChanged(ObjectSceneEvent e, Object oldFocus, Object newFocus) {
displayNodeButton.setEnabled(true);
invalidateButton.setEnabled(true);
+ dumpDisplayListButton.setEnabled(true);
requestLayoutButton.setEnabled(true);
Set<Object> selection = new HashSet<Object>();
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/DumpDisplayListAction.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/DumpDisplayListAction.java
new file mode 100644
index 0000000..3e66794
--- /dev/null
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/DumpDisplayListAction.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 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.hierarchyviewer.ui.action;
+
+import com.android.hierarchyviewer.ui.Workspace;
+
+import javax.swing.KeyStroke;
+import java.awt.event.KeyEvent;
+import java.awt.event.ActionEvent;
+import java.awt.Toolkit;
+
+public class DumpDisplayListAction extends BackgroundAction {
+ public static final String ACTION_NAME = "dumpDisplayList";
+ private Workspace mWorkspace;
+
+ public DumpDisplayListAction(Workspace workspace) {
+ putValue(NAME, "Dump DisplayList");
+ putValue(SHORT_DESCRIPTION, "Dump DisplayList");
+ putValue(LONG_DESCRIPTION, "Dump DisplayList");
+ this.mWorkspace = workspace;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ executeBackgroundTask(mWorkspace.outputDisplayList());
+ }
+}
diff --git a/hierarchyviewer2/app/.classpath b/hierarchyviewer2/app/.classpath
index d75889a..c5a657c 100644
--- a/hierarchyviewer2/app/.classpath
+++ b/hierarchyviewer2/app/.classpath
@@ -7,5 +7,6 @@
<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
<classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
+ <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/hierarchyviewer2/app/Android.mk b/hierarchyviewer2/app/Android.mk
index 2927f1d..0e00273 100644
--- a/hierarchyviewer2/app/Android.mk
+++ b/hierarchyviewer2/app/Android.mk
@@ -12,6 +12,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-HIERARCHYVIEWERAPP_LOCAL_DIR := $(call my-dir)
-include $(HIERARCHYVIEWERAPP_LOCAL_DIR)/etc/Android.mk
-include $(HIERARCHYVIEWERAPP_LOCAL_DIR)/src/Android.mk
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_JAR_MANIFEST := etc/manifest.txt
+
+LOCAL_JAVA_LIBRARIES := \
+ ddmlib \
+ ddmuilib \
+ hierarchyviewerlib \
+ swt \
+ org.eclipse.jface_3.4.2.M20090107-0800 \
+ org.eclipse.core.commands_3.4.0.I20080509-2000 \
+ sdklib \
+ swtmenubar
+
+LOCAL_MODULE := hierarchyviewer2
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hierarchyviewer2/app/NOTICE b/hierarchyviewer2/app/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/hierarchyviewer2/app/NOTICE
@@ -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/hierarchyviewer2/app/README b/hierarchyviewer2/app/README
new file mode 100755
index 0000000..c00ef99
--- /dev/null
+++ b/hierarchyviewer2/app/README
@@ -0,0 +1,69 @@
+Using the Eclipse project HierarchyViewer
+-----------------------------------------
+
+HierarchyViewer requires some external libraries to compile.
+If you build HierarchyViewer using the makefile, you have nothing
+to configure. However if you want to develop on HierarchyViewer
+using Eclipse, you need to perform the following configuration.
+
+
+-------
+1- Projects required in Eclipse
+-------
+
+To run HierarchyViewer from Eclipse, you need to import the following 5 projects:
+
+ - sdk/hierarchyviewer2/app
+ - sdk/hierarchyviewer2/libs/hierarchyviewerlib/
+ - sdk/ddms/libs/ddmlib
+ - sdk/ddms/libs/ddmuilib
+ - sdk/sdkmanager/libs/sdklib
+
+
+-------
+2- HierarchyViewer requires some SWT JARs to compile.
+-------
+
+SWT is available in the tree under prebuild/<platform>/swt
+
+Because the build path cannot contain relative path that are not inside
+the project directory, the .classpath file references a user library
+called ANDROID_SWT.
+
+In order to compile the project:
+- Open Preferences > Java > Build Path > User Libraries
+
+- Create a new user library named ANDROID_SWT
+- Add the following 4 JAR files:
+
+ - prebuilt/<platform>/swt/swt.jar
+ - prebuilt/common/eclipse/org.eclipse.core.commands_3.*.jar
+ - prebuilt/common/eclipse/org.eclipse.equinox.common_3.*.jar
+ - prebuilt/common/eclipse/org.eclipse.jface_3.*.jar
+
+
+-------
+3- HierarchyViewer also requires the compiled SwtMenuBar library.
+-------
+
+Build the swtmenubar library:
+$ cd $TOP (top of Android tree)
+$ . build/envsetup.sh && lunch sdk-eng
+$ sdk/eclipse/scripts/create_sdkman_symlinks.sh
+
+Define a classpath variable in Eclipse:
+- Open Preferences > Java > Build Path > Classpath Variables
+- Create a new classpath variable named ANDROID_OUT_FRAMEWORK
+- Set its folder value to <Android tree>/out/host/<platform>/framework
+- Create a new classpath variable named ANDROID_SRC
+- Set its folder value to <Android tree>
+
+You might need to clean the ddms project (Project > Clean...) after
+you add the new classpath variable, otherwise previous errors might not
+go away automatically.
+
+The ANDROID_SRC part should be optional. It allows you to have access to
+the SwtMenuBar generic parts from the Java editor.
+
+--
+EOF
diff --git a/hierarchyviewer2/app/etc/hierarchyviewer b/hierarchyviewer2/app/etc/hierarchyviewer
index 9f31701..82304dc 100755
--- a/hierarchyviewer2/app/etc/hierarchyviewer
+++ b/hierarchyviewer2/app/etc/hierarchyviewer
@@ -76,7 +76,7 @@ if [ `uname` = "Linux" ]; then
export GDK_NATIVE_WINDOWS=true
fi
-jarpath="$frameworkdir/$jarfile"
+jarpath="$frameworkdir/$jarfile:$frameworkdir/swtmenubar.jar"
# Figure out the path to the swt.jar for the current architecture.
# if ANDROID_SWT is defined, then just use this.
@@ -103,4 +103,8 @@ fi
# need to use "java.ext.dirs" because "-jar" causes classpath to be ignored
# might need more memory, e.g. -Xmx128M
-exec "$javaCmd" -Xmx512M $os_opts $java_debug -Dcom.android.hierarchyviewer.bindir="$progdir" -classpath "$jarpath:$swtpath/swt.jar" com.android.hierarchyviewer.HierarchyViewerApplication "$@"
+exec "$javaCmd" \
+ -Xmx512M $os_opts $java_debug \
+ -Dcom.android.hierarchyviewer.bindir="$progdir" \
+ -classpath "$jarpath:$swtpath/swt.jar" \
+ com.android.hierarchyviewer.HierarchyViewerApplication "$@"
diff --git a/hierarchyviewer2/app/etc/hierarchyviewer.bat b/hierarchyviewer2/app/etc/hierarchyviewer.bat
index 0876d2f..8a14957 100755
--- a/hierarchyviewer2/app/etc/hierarchyviewer.bat
+++ b/hierarchyviewer2/app/etc/hierarchyviewer.bat
@@ -49,7 +49,7 @@ if debug NEQ "%1" goto NoDebug
shift 1
:NoDebug
-set jarpath=%frameworkdir%%jarfile%;%frameworkdir%hierarchyviewerlib.jar
+set jarpath=%frameworkdir%%jarfile%;%frameworkdir%hierarchyviewerlib.jar;%frameworkdir%swtmenubar.jar
if not defined ANDROID_SWT goto QueryArch
set swt_path=%ANDROID_SWT%
diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/AboutDialog.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/AboutDialog.java
index 3f973e7..150c70a 100644
--- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/AboutDialog.java
+++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/AboutDialog.java
@@ -41,8 +41,8 @@ public class AboutDialog extends Dialog {
public AboutDialog(Shell shell) {
super(shell);
ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class);
- mSmallImage = imageLoader.loadImage("load-view-hierarchy.png", Display.getDefault()); //$NON-NLS-1$
- mAboutImage = imageLoader.loadImage("about.jpg", Display.getDefault()); //$NON-NLS-1$
+ mSmallImage = imageLoader.loadImage("sdk-hierarchyviewer-16.png", Display.getDefault()); //$NON-NLS-1$
+ mAboutImage = imageLoader.loadImage("sdk-hierarchyviewer-128.png", Display.getDefault()); //$NON-NLS-1$
}
@Override
diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java
index 25943c4..bf18965 100644
--- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java
+++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java
@@ -26,6 +26,7 @@ import com.android.hierarchyviewer.util.ActionButton;
import com.android.hierarchyviewerlib.HierarchyViewerDirector;
import com.android.hierarchyviewerlib.actions.CapturePSDAction;
import com.android.hierarchyviewerlib.actions.DisplayViewAction;
+import com.android.hierarchyviewerlib.actions.DumpDisplayListAction;
import com.android.hierarchyviewerlib.actions.InspectScreenshotAction;
import com.android.hierarchyviewerlib.actions.InvalidateAction;
import com.android.hierarchyviewerlib.actions.LoadOverlayAction;
@@ -38,6 +39,8 @@ import com.android.hierarchyviewerlib.actions.RefreshWindowsAction;
import com.android.hierarchyviewerlib.actions.RequestLayoutAction;
import com.android.hierarchyviewerlib.actions.SavePixelPerfectAction;
import com.android.hierarchyviewerlib.actions.SaveTreeViewAction;
+import com.android.hierarchyviewerlib.device.DeviceBridge.ViewServerInfo;
+import com.android.hierarchyviewerlib.models.DeviceSelectionModel;
import com.android.hierarchyviewerlib.models.PixelPerfectModel;
import com.android.hierarchyviewerlib.models.TreeViewModel;
import com.android.hierarchyviewerlib.models.PixelPerfectModel.IImageChangeListener;
@@ -53,11 +56,15 @@ import com.android.hierarchyviewerlib.ui.PropertyViewer;
import com.android.hierarchyviewerlib.ui.TreeView;
import com.android.hierarchyviewerlib.ui.TreeViewControls;
import com.android.hierarchyviewerlib.ui.TreeViewOverview;
+import com.android.menubar.IMenuBarEnhancer;
+import com.android.menubar.MenuBarEnhancer;
+import com.android.menubar.IMenuBarEnhancer.MenuBarMode;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.SelectionEvent;
@@ -81,8 +88,9 @@ import org.eclipse.swt.widgets.Shell;
public class HierarchyViewerApplication extends ApplicationWindow {
- private static final int INITIAL_WIDTH = 1024;
- private static final int INITIAL_HEIGHT = 768;
+ private static final String APP_NAME = "Hierarchy Viewer";
+ private static final int INITIAL_WIDTH = 1280;
+ private static final int INITIAL_HEIGHT = 800;
private static HierarchyViewerApplication sMainWindow;
@@ -119,6 +127,8 @@ public class HierarchyViewerApplication extends ApplicationWindow {
private PixelPerfectLoupe mPixelPerfectLoupe;
private Composite mTreeViewControls;
+ private ActionButton dumpDisplayList;
+
private HierarchyViewerDirector mDirector;
/*
@@ -148,9 +158,9 @@ public class HierarchyViewerApplication extends ApplicationWindow {
@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
- shell.setText("Hierarchy Viewer");
+ shell.setText(APP_NAME);
ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class);
- Image image = imageLoader.loadImage("load-view-hierarchy.png", Display.getDefault()); //$NON-NLS-1$
+ Image image = imageLoader.loadImage("sdk-hierarchyviewer-128.png", Display.getDefault()); //$NON-NLS-1$
shell.setImage(image);
}
@@ -162,7 +172,14 @@ public class HierarchyViewerApplication extends ApplicationWindow {
public void run() {
setBlockOnOpen(true);
- open();
+ try {
+ open();
+ } catch (SWTException e) {
+ // Ignore "widget disposed" errors after we closed.
+ if (!getShell().isDisposed()) {
+ throw e;
+ }
+ }
TreeViewModel.getModel().removeTreeChangeListener(mTreeChangeListener);
PixelPerfectModel.getModel().removeImageChangeListener(mImageChangeListener);
@@ -354,7 +371,7 @@ public class HierarchyViewerApplication extends ApplicationWindow {
Composite innerButtonPanel = new Composite(buttonPanel, SWT.NONE);
innerButtonPanel.setLayoutData(new GridData(GridData.FILL_VERTICAL));
- GridLayout innerButtonPanelLayout = new GridLayout(6, true);
+ GridLayout innerButtonPanelLayout = new GridLayout(7, true);
innerButtonPanelLayout.marginWidth = innerButtonPanelLayout.marginHeight = 2;
innerButtonPanelLayout.horizontalSpacing = innerButtonPanelLayout.verticalSpacing = 2;
innerButtonPanel.setLayout(innerButtonPanelLayout);
@@ -382,6 +399,10 @@ public class HierarchyViewerApplication extends ApplicationWindow {
new ActionButton(innerButtonPanel, RequestLayoutAction.getAction());
requestLayout.setLayoutData(new GridData(GridData.FILL_BOTH));
+ dumpDisplayList =
+ new ActionButton(innerButtonPanel, DumpDisplayListAction.getAction());
+ dumpDisplayList.setLayoutData(new GridData(GridData.FILL_BOTH));
+
SashForm mainSash = new SashForm(mTreeViewPanel, SWT.HORIZONTAL | SWT.SMOOTH);
mainSash.setLayoutData(new GridData(GridData.FILL_BOTH));
Composite treeViewContainer = new Composite(mainSash, SWT.BORDER);
@@ -581,12 +602,16 @@ public class HierarchyViewerApplication extends ApplicationWindow {
MenuManager mm = getMenuBarManager();
mm.removeAll();
- String os = System.getProperty("os.name"); //$NON-NLS-1$
- if (os.startsWith("Mac OS") == false) { //$NON-NLS-1$
- MenuManager file = new MenuManager("&File");
+ MenuManager file = new MenuManager("&File");
+ IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenuManager(
+ APP_NAME,
+ getShell().getDisplay(),
+ file,
+ AboutAction.getAction(getShell()),
+ null /*preferencesAction*/,
+ QuitAction.getAction());
+ if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
mm.add(file);
-
- file.add(QuitAction.getAction());
}
MenuManager device = new MenuManager("&Devices");
@@ -596,11 +621,6 @@ public class HierarchyViewerApplication extends ApplicationWindow {
device.add(LoadViewHierarchyAction.getAction());
device.add(InspectScreenshotAction.getAction());
- MenuManager help = new MenuManager("&Help");
- mm.add(help);
-
- help.add(AboutAction.getAction(getShell()));
-
mm.updateAll(true);
mDeviceViewButton.setSelection(true);
@@ -626,12 +646,16 @@ public class HierarchyViewerApplication extends ApplicationWindow {
MenuManager mm = getMenuBarManager();
mm.removeAll();
- String os = System.getProperty("os.name"); //$NON-NLS-1$
- if (os.startsWith("Mac OS") == false) { //$NON-NLS-1$
- MenuManager file = new MenuManager("&File");
+ MenuManager file = new MenuManager("&File");
+ IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenuManager(
+ APP_NAME,
+ getShell().getDisplay(),
+ file,
+ AboutAction.getAction(getShell()),
+ null /*preferencesAction*/,
+ QuitAction.getAction());
+ if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
mm.add(file);
-
- file.add(QuitAction.getAction());
}
MenuManager treeViewMenu = new MenuManager("&Tree View");
@@ -642,15 +666,18 @@ public class HierarchyViewerApplication extends ApplicationWindow {
treeViewMenu.add(new Separator());
treeViewMenu.add(RefreshViewAction.getAction());
treeViewMenu.add(DisplayViewAction.getAction(getShell()));
+ // Only make the DumpDisplayList action visible if the protocol supports it.
+ ViewServerInfo info = DeviceSelectionModel.getModel().getSelectedDeviceInfo();
+ if (info != null && info.protocolVersion >= 4) {
+ treeViewMenu.add(DumpDisplayListAction.getAction());
+ dumpDisplayList.setVisible(true);
+ } else {
+ dumpDisplayList.setVisible(false);
+ }
treeViewMenu.add(new Separator());
treeViewMenu.add(InvalidateAction.getAction());
treeViewMenu.add(RequestLayoutAction.getAction());
- MenuManager help = new MenuManager("&Help");
- mm.add(help);
-
- help.add(AboutAction.getAction(getShell()));
-
mm.updateAll(true);
mDeviceViewButton.setSelection(false);
@@ -676,12 +703,16 @@ public class HierarchyViewerApplication extends ApplicationWindow {
MenuManager mm = getMenuBarManager();
mm.removeAll();
- String os = System.getProperty("os.name"); //$NON-NLS-1$
- if (os.startsWith("Mac OS") == false) { //$NON-NLS-1$
- MenuManager file = new MenuManager("&File");
+ MenuManager file = new MenuManager("&File");
+ IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenuManager(
+ APP_NAME,
+ getShell().getDisplay(),
+ file,
+ AboutAction.getAction(getShell()),
+ null /*preferencesAction*/,
+ QuitAction.getAction());
+ if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
mm.add(file);
-
- file.add(QuitAction.getAction());
}
MenuManager pixelPerfect = new MenuManager("&Pixel Perfect");
@@ -695,11 +726,6 @@ public class HierarchyViewerApplication extends ApplicationWindow {
mm.add(pixelPerfect);
- MenuManager help = new MenuManager("&Help");
- mm.add(help);
-
- help.add(AboutAction.getAction(getShell()));
-
mm.updateAll(true);
mDeviceViewButton.setSelection(false);
diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/actions/AboutAction.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/actions/AboutAction.java
index 0c7c7b2..332b2dc 100644
--- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/actions/AboutAction.java
+++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/actions/AboutAction.java
@@ -41,7 +41,7 @@ public class AboutAction extends Action implements ImageAction {
this.mShell = shell;
setAccelerator(SWT.MOD1 + 'A');
ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class);
- mImage = imageLoader.loadImage("about-small.jpg", Display.getDefault()); //$NON-NLS-1$
+ mImage = imageLoader.loadImage("sdk-hierarchyviewer-16.png", Display.getDefault()); //$NON-NLS-1$
setImageDescriptor(ImageDescriptor.createFromImage(mImage));
setToolTipText("Shows the about dialog");
}
diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/util/ActionButton.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/util/ActionButton.java
index 4681c40..ca3c689 100644
--- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/util/ActionButton.java
+++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/util/ActionButton.java
@@ -73,4 +73,8 @@ public class ActionButton implements IPropertyChangeListener, SelectionListener
public void addSelectionListener(SelectionListener listener) {
mButton.addSelectionListener(listener);
}
+
+ public void setVisible(boolean visible) {
+ mButton.setVisible(visible);
+ }
}
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/NOTICE b/hierarchyviewer2/libs/hierarchyviewerlib/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/NOTICE
@@ -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/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk
index ded20e1..3ca63dd 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk
@@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_JAVA_RESOURCE_DIRS := resources
+LOCAL_JAVA_RESOURCE_DIRS := ../src
LOCAL_JAR_MANIFEST := ../manifest.txt
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java
index 77f8d74..23dfbea 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java
@@ -166,7 +166,7 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener,
return;
}
Window[] windows = DeviceBridge.loadWindows(device);
- DeviceSelectionModel.getModel().addDevice(device, windows);
+ DeviceSelectionModel.getModel().addDevice(device, windows, viewServerInfo);
if (viewServerInfo.protocolVersion >= 3) {
WindowUpdater.startListenForWindowChanges(HierarchyViewerDirector.this, device);
focusChanged(device);
@@ -586,6 +586,17 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener,
}
}
+ public void dumpDisplayListForCurrentNode() {
+ final DrawableViewNode selectedNode = TreeViewModel.getModel().getSelection();
+ if (selectedNode != null) {
+ executeInBackground("Dump displaylist", new Runnable() {
+ public void run() {
+ DeviceBridge.outputDisplayList(selectedNode.viewNode);
+ }
+ });
+ }
+ }
+
public void loadAllViews() {
executeInBackground("Loading all views", new Runnable() {
public void run() {
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/actions/DumpDisplayListAction.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/actions/DumpDisplayListAction.java
new file mode 100644
index 0000000..8b9ba29
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/actions/DumpDisplayListAction.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 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.hierarchyviewerlib.actions;
+
+import com.android.ddmuilib.ImageLoader;
+import com.android.hierarchyviewerlib.HierarchyViewerDirector;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+
+public class DumpDisplayListAction extends SelectedNodeEnabledAction implements ImageAction {
+
+ private static DumpDisplayListAction sAction;
+
+ private Image mImage;
+
+ private DumpDisplayListAction() {
+ super("Dump DisplayList");
+ ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class);
+ mImage = imageLoader.loadImage("load-view-hierarchy.png", Display.getDefault()); //$NON-NLS-1$
+ setImageDescriptor(ImageDescriptor.createFromImage(mImage));
+ setToolTipText("Request the view to output its displaylist to logcat");
+ }
+
+ public static DumpDisplayListAction getAction() {
+ if (sAction == null) {
+ sAction = new DumpDisplayListAction();
+ }
+ return sAction;
+ }
+
+ @Override
+ public void run() {
+ HierarchyViewerDirector.getDirector().dumpDisplayListForCurrentNode();
+ }
+
+ public Image getImage() {
+ return mImage;
+ }
+}
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java
index 40cc3a9..610f7b3 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java
@@ -643,4 +643,18 @@ public class DeviceBridge {
}
}
+ public static void outputDisplayList(ViewNode viewNode) {
+ DeviceConnection connection = null;
+ try {
+ connection = new DeviceConnection(viewNode.window.getDevice());
+ connection.sendCommand("OUTPUT_DISPLAYLIST " +
+ viewNode.window.encode() + " " + viewNode); //$NON-NLS-1$
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to dump displaylist for node " + viewNode + " in window "
+ + viewNode.window + " on device " + viewNode.window.getDevice());
+ } finally {
+ connection.close();
+ }
+ }
+
}
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/DeviceSelectionModel.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/DeviceSelectionModel.java
index d029d39..b00a1dc 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/DeviceSelectionModel.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/DeviceSelectionModel.java
@@ -17,6 +17,7 @@
package com.android.hierarchyviewerlib.models;
import com.android.ddmlib.IDevice;
+import com.android.hierarchyviewerlib.device.DeviceBridge.ViewServerInfo;
import com.android.hierarchyviewerlib.device.Window;
import java.util.ArrayList;
@@ -29,7 +30,7 @@ import java.util.HashMap;
*/
public class DeviceSelectionModel {
- private final HashMap<IDevice, Window[]> mDeviceMap = new HashMap<IDevice, Window[]>();
+ private final HashMap<IDevice, DeviceInfo> mDeviceMap = new HashMap<IDevice, DeviceInfo>();
private final HashMap<IDevice, Integer> mFocusedWindowHashes = new HashMap<IDevice, Integer>();
@@ -44,6 +45,15 @@ public class DeviceSelectionModel {
private static DeviceSelectionModel sModel;
+ private static class DeviceInfo {
+ Window[] windows;
+ ViewServerInfo viewServerInfo;
+
+ private DeviceInfo(Window[] windows, ViewServerInfo viewServerInfo) {
+ this.windows = windows;
+ this.viewServerInfo = viewServerInfo;
+ }
+ }
public static DeviceSelectionModel getModel() {
if (sModel == null) {
sModel = new DeviceSelectionModel();
@@ -57,9 +67,9 @@ public class DeviceSelectionModel {
}
}
- public void addDevice(IDevice device, Window[] windows) {
+ public void addDevice(IDevice device, Window[] windows, ViewServerInfo info) {
synchronized (mDeviceMap) {
- mDeviceMap.put(device, windows);
+ mDeviceMap.put(device, new DeviceInfo(windows, info));
mDeviceList.add(device);
}
notifyDeviceConnected(device);
@@ -88,7 +98,12 @@ public class DeviceSelectionModel {
public void updateDevice(IDevice device, Window[] windows) {
boolean selectionChanged = false;
synchronized (mDeviceMap) {
- mDeviceMap.put(device, windows);
+ DeviceInfo oldDeviceInfo = mDeviceMap.get(device);
+ ViewServerInfo oldViewServerInfo = null;
+ if (oldDeviceInfo != null) {
+ oldViewServerInfo = oldDeviceInfo.viewServerInfo;
+ }
+ mDeviceMap.put(device, new DeviceInfo(windows, oldViewServerInfo));
// If the selected window no longer exists, we clear the selection.
if (mSelectedDevice == device && mSelectedWindow != null) {
boolean windowStillExists = false;
@@ -214,9 +229,12 @@ public class DeviceSelectionModel {
}
public Window[] getWindows(IDevice device) {
- Window[] windows;
+ Window[] windows = null;
synchronized (mDeviceMap) {
- windows = mDeviceMap.get(device);
+ DeviceInfo info = mDeviceMap.get(device);
+ if (info != null) {
+ windows = mDeviceMap.get(device).windows;
+ }
}
return windows;
}
@@ -253,4 +271,15 @@ public class DeviceSelectionModel {
return mSelectedWindow;
}
}
+
+ public ViewServerInfo getSelectedDeviceInfo() {
+ synchronized (mDeviceMap) {
+ ViewServerInfo viewServerInfo = null;
+ if (mSelectedDevice != null) {
+ return mDeviceMap.get(mSelectedDevice).viewServerInfo;
+ }
+ return null;
+ }
+ }
+
}
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/auto-refresh.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/auto-refresh.png
index 240862f..240862f 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/auto-refresh.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/auto-refresh.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/capture-psd.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/capture-psd.png
index 0f25426..0f25426 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/capture-psd.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/capture-psd.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view-selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view-selected.png
index fd107ed..fd107ed 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view-selected.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view-selected.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view.png
index 9a7eed4..9a7eed4 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/display.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/display.png
index a9de0ec..a9de0ec 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/display.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/display.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/filtered.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/filtered.png
index 4fcab3f..4fcab3f 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/filtered.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/filtered.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/green.png
index 800000d..800000d 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/green.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/inspect-screenshot.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/inspect-screenshot.png
index 6e51701..6e51701 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/inspect-screenshot.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/inspect-screenshot.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/invalidate.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/invalidate.png
index ee75f69..ee75f69 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/invalidate.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/invalidate.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-all-views.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-all-views.png
index 3329ec9..3329ec9 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-all-views.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-all-views.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-overlay.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-overlay.png
index 4817252..4817252 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-overlay.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-overlay.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-view-hierarchy.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-view-hierarchy.png
index 8f01dda..8f01dda 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-view-hierarchy.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-view-hierarchy.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/not-selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/not-selected.png
index db6f13b..db6f13b 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/not-selected.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/not-selected.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-black.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-black.png
index cd88803..cd88803 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-black.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-black.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-white.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-white.png
index 5f05662..5f05662 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-white.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-white.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view-selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view-selected.png
index 1e44000..1e44000 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view-selected.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view-selected.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view.png
index ec51cec..ec51cec 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/red.png
index a2ab855..a2ab855 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/red.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/refresh-windows.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/refresh-windows.png
index 8fddcae..8fddcae 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/refresh-windows.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/refresh-windows.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/request-layout.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/request-layout.png
index 92a78c8..92a78c8 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/request-layout.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/request-layout.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/save.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/save.png
index 2c0bab1..2c0bab1 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/save.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/save.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-128.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-128.png
new file mode 100644
index 0000000..4535f22
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-128.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-16.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-16.png
new file mode 100755
index 0000000..8c3c23d
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-16.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered-small.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered-small.png
index 9ef6b34..9ef6b34 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered-small.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered-small.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered.png
index 1f59685..1f59685 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-small.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-small.png
index 538e385..538e385 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-small.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-small.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected.png
index 5cd5c3f..5cd5c3f 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-extras.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-extras.png
index ba9c305..ba9c305 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-extras.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-extras.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-overlay.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-overlay.png
index e39e90a..e39e90a 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-overlay.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-overlay.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view-selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view-selected.png
index 175ad1f..175ad1f 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view-selected.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view-selected.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view.png
index 23aa424..23aa424 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/yellow.png
index e9b5781..e9b5781 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/yellow.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about-small.jpg b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about-small.jpg
deleted file mode 100644
index 6fe9291..0000000
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about-small.jpg
+++ /dev/null
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about.jpg b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about.jpg
deleted file mode 100644
index 8e10514..0000000
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about.jpg
+++ /dev/null
Binary files differ
diff --git a/ide_common/.settings/org.eclipse.jdt.core.prefs b/ide_common/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/ide_common/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/ide_common/NOTICE b/ide_common/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/ide_common/NOTICE
@@ -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/ide_common/src/com/android/ide/common/rendering/LayoutLibrary.java b/ide_common/src/com/android/ide/common/rendering/LayoutLibrary.java
index d9ffda7..e1256fe 100644
--- a/ide_common/src/com/android/ide/common/rendering/LayoutLibrary.java
+++ b/ide_common/src/com/android/ide/common/rendering/LayoutLibrary.java
@@ -16,6 +16,8 @@
package com.android.ide.common.rendering;
+import static com.android.ide.common.rendering.api.Result.Status.NOT_IMPLEMENTED;
+
import com.android.ide.common.log.ILogger;
import com.android.ide.common.rendering.api.Bridge;
import com.android.ide.common.rendering.api.Capability;
@@ -321,6 +323,55 @@ public class LayoutLibrary {
}
}
+ /**
+ * Utility method returning the parent of a given view object.
+ *
+ * @param viewObject the object for which to return the parent.
+ *
+ * @return a {@link Result} indicating the status of the action, and if success, the parent
+ * object in {@link Result#getData()}
+ */
+ public Result getViewParent(Object viewObject) {
+ if (mBridge != null) {
+ return mBridge.getViewParent(viewObject);
+ }
+
+ return NOT_IMPLEMENTED.createResult();
+ }
+
+ /**
+ * Utility method returning the index of a given view in its parent.
+ * @param viewObject the object for which to return the index.
+ *
+ * @return a {@link Result} indicating the status of the action, and if success, the index in
+ * the parent in {@link Result#getData()}
+ */
+ public Result getViewIndex(Object viewObject) {
+ if (mBridge != null) {
+ return mBridge.getViewIndex(viewObject);
+ }
+
+ return NOT_IMPLEMENTED.createResult();
+ }
+
+ /**
+ * Utility method returning the baseline value for a given view object. This basically returns
+ * View.getBaseline().
+ *
+ * @param viewObject the object for which to return the index.
+ *
+ * @return the baseline value or -1 if not applicable to the view object or if this layout
+ * library does not implement this method.
+ */
+ public int getViewBaseline(Object viewObject) {
+ if (mBridge != null) {
+ return mBridge.getViewBaseline(viewObject);
+ }
+
+ return -1;
+ }
+
+
// ------ Implementation
private LayoutLibrary(Bridge bridge, ILayoutBridge legacyBridge, ClassLoader classLoader,
@@ -462,7 +513,7 @@ public class LayoutLibrary {
for (Entry<ResourceType, Map<String, ResourceValue>> entry : map.entrySet()) {
// ugly case but works.
result.put(entry.getKey().getName(),
- (Map<String, IResourceValue>)(Map) entry.getValue());
+ (Map) entry.getValue());
}
return result;
diff --git a/ide_common/src/com/android/ide/common/resources/FrameworkResourceItem.java b/ide_common/src/com/android/ide/common/resources/FrameworkResourceItem.java
new file mode 100644
index 0000000..70bbcef
--- /dev/null
+++ b/ide_common/src/com/android/ide/common/resources/FrameworkResourceItem.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.ide.common.resources;
+
+/**
+ * A custom {@link ResourceItem} for resources provided by the framework.
+ *
+ * The main change is that {@link #isEditableDirectly()} returns false.
+ */
+class FrameworkResourceItem extends ResourceItem {
+
+ FrameworkResourceItem(String name) {
+ super(name);
+ }
+
+ @Override
+ public boolean isEditableDirectly() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "FrameworkResourceItem [mName=" + getName() + ", mFiles=" //$NON-NLS-1$ //$NON-NLS-2$
+ + getSourceFileList() + "]"; //$NON-NLS-1$
+ }
+}
diff --git a/ide_common/src/com/android/ide/common/resources/FrameworkResources.java b/ide_common/src/com/android/ide/common/resources/FrameworkResources.java
new file mode 100644
index 0000000..31dc137
--- /dev/null
+++ b/ide_common/src/com/android/ide/common/resources/FrameworkResources.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2011 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.ide.common.resources;
+
+import static com.android.AndroidConstants.FD_RES_VALUES;
+
+import com.android.ide.common.log.ILogger;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.resources.ResourceType;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * Framework resources repository.
+ *
+ * This behaves the same as {@link ResourceRepository} except that it differentiates between
+ * resources that are public and non public.
+ * {@link #getResources(ResourceType)} and {@link #hasResourcesOfType(ResourceType)} only return
+ * public resources. This is typically used to display resource lists in the UI.
+ *
+ * {@link #getConfiguredResources(com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration)}
+ * returns all resources, even the non public ones so that this can be used for rendering.
+ */
+public class FrameworkResources extends ResourceRepository {
+
+ /**
+ * Map of {@link ResourceType} to list of items. It is guaranteed to contain a list for all
+ * possible values of ResourceType.
+ */
+ protected final Map<ResourceType, List<ResourceItem>> mPublicResourceMap =
+ new EnumMap<ResourceType, List<ResourceItem>>(ResourceType.class);
+
+ public FrameworkResources() {
+ super(true /*isFrameworkRepository*/);
+ }
+
+ /**
+ * Returns a {@link Collection} (always non null, but can be empty) of <b>public</b>
+ * {@link ResourceItem} matching a given {@link ResourceType}.
+ *
+ * @param type the type of the resources to return
+ * @return a collection of items, possible empty.
+ */
+ @Override
+ public List<ResourceItem> getResourceItemsOfType(ResourceType type) {
+ return mPublicResourceMap.get(type);
+ }
+
+ /**
+ * Returns whether the repository has <b>public</b> resources of a given {@link ResourceType}.
+ * @param type the type of resource to check.
+ * @return true if the repository contains resources of the given type, false otherwise.
+ */
+ @Override
+ public boolean hasResourcesOfType(ResourceType type) {
+ return mPublicResourceMap.get(type).size() > 0;
+ }
+
+ @Override
+ protected ResourceItem createResourceItem(String name) {
+ return new FrameworkResourceItem(name);
+ }
+
+ /**
+ * Reads the public.xml file in data/res/values/ for a given resource folder and builds up
+ * a map of public resources.
+ *
+ * This map is a subset of the full resource map that only contains framework resources
+ * that are public.
+ *
+ * @param osFrameworkResourcePath The root folder of the resources
+ */
+ public void loadPublicResources(IAbstractFolder resFolder, ILogger logger) {
+ IAbstractFolder valueFolder = resFolder.getFolder(FD_RES_VALUES);
+ if (valueFolder.exists() == false) {
+ return;
+ }
+
+ IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); //$NON-NLS-1$
+ if (publicXmlFile.exists()) {
+ Document document = null;
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ Reader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(publicXmlFile.getContents()));
+ InputSource is = new InputSource(reader);
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ document = builder.parse(is);
+
+ ResourceType lastType = null;
+ String lastTypeName = "";
+
+ NodeList children = document.getDocumentElement().getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element element = (Element) node;
+ String name = element.getAttribute("name"); //$NON-NLS-1$
+ if (name.length() > 0) {
+ String typeName = element.getAttribute("type"); //$NON-NLS-1$
+ ResourceType type = null;
+ if (typeName.equals(lastTypeName)) {
+ type = lastType;
+ } else {
+ type = ResourceType.getEnum(typeName);
+ lastType = type;
+ lastTypeName = typeName;
+ }
+ if (type != null) {
+ List<ResourceItem> typeList = mResourceMap.get(type);
+
+ ResourceItem match = null;
+ if (typeList != null) {
+ for (ResourceItem item : typeList) {
+ if (name.equals(item.getName())) {
+ match = item;
+ break;
+ }
+ }
+ }
+
+ if (match != null) {
+ List<ResourceItem> publicList = mPublicResourceMap.get(type);
+ if (publicList == null) {
+ publicList = new ArrayList<ResourceItem>();
+ mPublicResourceMap.put(type, publicList);
+ }
+
+ publicList.add(match);
+ } else {
+ // log that there's a public resource that doesn't actually
+ // exist?
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ if (logger != null) {
+ logger.error(e, "Can't read and parse public attribute list");
+ }
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // Nothing to be done here - we don't care if it closed or not.
+ }
+ }
+ }
+ }
+
+ // put unmodifiable list for all res type in the public resource map
+ // this will simplify access
+ for (ResourceType type : ResourceType.values()) {
+ List<ResourceItem> list = mPublicResourceMap.get(type);
+ if (list == null) {
+ list = Collections.emptyList();
+ } else {
+ list = Collections.unmodifiableList(list);
+ }
+
+ // put the new list in the map
+ mPublicResourceMap.put(type, list);
+ }
+ }
+}
diff --git a/ide_common/src/com/android/ide/common/resources/InlineResourceItem.java b/ide_common/src/com/android/ide/common/resources/InlineResourceItem.java
new file mode 100644
index 0000000..37fdc81
--- /dev/null
+++ b/ide_common/src/com/android/ide/common/resources/InlineResourceItem.java
@@ -0,0 +1,71 @@
+/*
+ * 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.android.ide.common.resources;
+
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.resources.ResourceType;
+
+
+/**
+ * Represents a resource item that has been declared inline in another resource file.
+ *
+ * This covers the typical ID declaration of "@+id/foo", but does not cover normal value
+ * resources declared in strings.xml or other similar value files.
+ *
+ * This resource will return {@code true} for {@link #isDeclaredInline()} and {@code false} for
+ * {@link #isEditableDirectly()}.
+ */
+public class InlineResourceItem extends ResourceItem {
+
+ private ResourceValue mValue = null;
+
+ /**
+ * Constructs a new inline ResourceItem.
+ * @param name the name of the resource as it appears in the XML and R.java files.
+ */
+ public InlineResourceItem(String name) {
+ super(name);
+ }
+
+ @Override
+ public boolean isDeclaredInline() {
+ return true;
+ }
+
+ @Override
+ public boolean isEditableDirectly() {
+ return false;
+ }
+
+ @Override
+ public ResourceValue getResourceValue(ResourceType type, FolderConfiguration referenceConfig,
+ boolean isFramework) {
+ assert type == ResourceType.ID;
+ if (mValue == null) {
+ mValue = new ResourceValue(type, getName(), isFramework);
+ }
+
+ return mValue;
+ }
+
+ @Override
+ public String toString() {
+ return "InlineResourceItem [mName=" + getName() + ", mFiles=" //$NON-NLS-1$ //$NON-NLS-2$
+ + getSourceFileList() + "]"; //$NON-NLS-1$
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IntArrayWrapper.java b/ide_common/src/com/android/ide/common/resources/IntArrayWrapper.java
index 3e614ed..668c677 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IntArrayWrapper.java
+++ b/ide_common/src/com/android/ide/common/resources/IntArrayWrapper.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.manager;
+package com.android.ide.common.resources;
import java.util.Arrays;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java b/ide_common/src/com/android/ide/common/resources/MultiResourceFile.java
index a3223c0..c6bfeff 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java
+++ b/ide_common/src/com/android/ide/common/resources/MultiResourceFile.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,24 +14,22 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.manager;
+package com.android.ide.common.resources;
import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.resources.ValueResourceParser;
import com.android.ide.common.resources.ValueResourceParser.IValueResourceRepository;
+import com.android.io.IAbstractFile;
+import com.android.io.StreamException;
import com.android.resources.ResourceType;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.StreamException;
import org.xml.sax.SAXException;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
-import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
@@ -47,72 +45,79 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
private final static SAXParserFactory sParserFactory = SAXParserFactory.newInstance();
- private final Map<ResourceType, HashMap<String, ResourceValue>> mResourceItems =
- new EnumMap<ResourceType, HashMap<String, ResourceValue>>(ResourceType.class);
+ private final Map<ResourceType, Map<String, ResourceValue>> mResourceItems =
+ new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class);
+
+ private Collection<ResourceType> mResourceTypeList = null;
public MultiResourceFile(IAbstractFile file, ResourceFolder folder) {
super(file, folder);
}
@Override
- public ResourceType[] getResourceTypes() {
- update();
+ protected void load() {
+ // need to parse the file and find the content.
+ parseFile();
- Set<ResourceType> keys = mResourceItems.keySet();
+ // create new ResourceItems for the new content.
+ mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
- return keys.toArray(new ResourceType[keys.size()]);
+ // create/update the resource items.
+ updateResourceItems();
}
@Override
- public boolean hasResources(ResourceType type) {
- update();
+ protected void update() {
+ // remove this file from all existing ResourceItem.
+ getFolder().getRepository().removeFile(mResourceTypeList, this);
- HashMap<String, ResourceValue> list = mResourceItems.get(type);
- return (list != null && list.size() > 0);
- }
+ // reset current content.
+ mResourceItems.clear();
- @Override
- public Collection<ProjectResourceItem> getResources(ResourceType type,
- ProjectResources projectResources) {
- update();
+ // need to parse the file and find the content.
+ parseFile();
- HashMap<String, ResourceValue> list = mResourceItems.get(type);
+ // create new ResourceItems for the new content.
+ mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
- ArrayList<ProjectResourceItem> items = new ArrayList<ProjectResourceItem>();
+ // create/update the resource items.
+ updateResourceItems();
+ }
- if (list != null) {
- Collection<ResourceValue> values = list.values();
- for (ResourceValue res : values) {
- ProjectResourceItem item = projectResources.findResourceItem(type, res.getName());
-
- if (item == null) {
- if (type == ResourceType.ID) {
- item = new IdResourceItem(res.getName(), false /* isDeclaredInline */);
- } else {
- item = new ConfigurableResourceItem(res.getName());
- }
- items.add(item);
- }
+ @Override
+ protected void dispose() {
+ // only remove this file from all existing ResourceItem.
+ getFolder().getRepository().removeFile(mResourceTypeList, this);
- item.add(this);
- }
- }
+ // don't need to touch the content, it'll get reclaimed as this objects disappear.
+ // In the mean time other objects may need to access it.
+ }
- return items;
+ @Override
+ public Collection<ResourceType> getResourceTypes() {
+ return mResourceTypeList;
}
- /**
- * Updates the Resource items if necessary.
- */
- private void update() {
- if (isTouched() == true) {
- // reset current content.
- mResourceItems.clear();
+ @Override
+ public boolean hasResources(ResourceType type) {
+ Map<String, ResourceValue> list = mResourceItems.get(type);
+ return (list != null && list.size() > 0);
+ }
+
+ private void updateResourceItems() {
+ ResourceRepository repository = getRepository();
+ for (ResourceType type : mResourceTypeList) {
+ Map<String, ResourceValue> list = mResourceItems.get(type);
- // need to parse the file and find the content.
- parseFile();
+ if (list != null) {
+ Collection<ResourceValue> values = list.values();
+ for (ResourceValue res : values) {
+ ResourceItem item = repository.getResourceItem(type, res.getName());
- resetTouch();
+ // add this file to the list of files generating this resource item.
+ item.add(this);
+ }
+ }
}
}
@@ -136,7 +141,7 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
* @param value The value of the resource.
*/
public void addResourceValue(ResourceType resType, ResourceValue value) {
- HashMap<String, ResourceValue> list = mResourceItems.get(resType);
+ Map<String, ResourceValue> list = mResourceItems.get(resType);
// if the list does not exist, create it.
if (list == null) {
@@ -158,10 +163,8 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
@Override
public ResourceValue getValue(ResourceType type, String name) {
- update();
-
// get the list for the given type
- HashMap<String, ResourceValue> list = mResourceItems.get(type);
+ Map<String, ResourceValue> list = mResourceItems.get(type);
if (list != null) {
return list.get(name);
diff --git a/ide_common/src/com/android/ide/common/resources/ResourceDeltaKind.java b/ide_common/src/com/android/ide/common/resources/ResourceDeltaKind.java
new file mode 100644
index 0000000..769b6ea
--- /dev/null
+++ b/ide_common/src/com/android/ide/common/resources/ResourceDeltaKind.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 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.ide.common.resources;
+
+/**
+ * Enum indicating a type of resource change.
+ *
+ * This is similar, and can be easily mapped to Eclipse's integer constants in IResourceDelta.
+ */
+public enum ResourceDeltaKind {
+ CHANGED, ADDED, REMOVED;
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java b/ide_common/src/com/android/ide/common/resources/ResourceFile.java
index b754201..03f0b34 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java
+++ b/ide_common/src/com/android/ide/common/resources/ResourceFile.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,19 +14,20 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.manager;
+package com.android.ide.common.resources;
import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.ide.common.resources.configuration.Configurable;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.io.IAbstractFile;
import com.android.resources.ResourceType;
-import com.android.sdklib.io.IAbstractFile;
import java.util.Collection;
/**
* Represents a Resource file (a file under $Project/res/)
*/
-public abstract class ResourceFile extends Resource {
+public abstract class ResourceFile implements Configurable {
private final IAbstractFile mFile;
private final ResourceFolder mFolder;
@@ -36,11 +37,10 @@ public abstract class ResourceFile extends Resource {
mFolder = folder;
}
- /*
- * (non-Javadoc)
- * @see com.android.ide.eclipse.editors.resources.manager.Resource#getConfiguration()
- */
- @Override
+ protected abstract void load();
+ protected abstract void update();
+ protected abstract void dispose();
+
public FolderConfiguration getConfiguration() {
return mFolder.getConfiguration();
}
@@ -59,17 +59,21 @@ public abstract class ResourceFile extends Resource {
return mFolder;
}
+ public final ResourceRepository getRepository() {
+ return mFolder.getRepository();
+ }
+
/**
* Returns whether the resource is a framework resource.
*/
public final boolean isFramework() {
- return mFolder.isFramework();
+ return mFolder.getRepository().isFrameworkRepository();
}
/**
- * Returns the list of {@link ResourceType} generated by the file.
+ * Returns the list of {@link ResourceType} generated by the file. This is never null.
*/
- public abstract ResourceType[] getResourceTypes();
+ public abstract Collection<ResourceType> getResourceTypes();
/**
* Returns whether the file generated a resource of a specific type.
@@ -78,18 +82,6 @@ public abstract class ResourceFile extends Resource {
public abstract boolean hasResources(ResourceType type);
/**
- * Get the list of {@link ProjectResourceItem} of a specific type generated by the file.
- * This method must make sure not to create duplicate.
- * @param type The type of {@link ProjectResourceItem} to return.
- * @param projectResources The global Project Resource object, allowing the implementation to
- * query for already existing {@link ProjectResourceItem}
- * @return The list of <b>new</b> {@link ProjectResourceItem}
- * @see ProjectResources#findResourceItem(ResourceType, String)
- */
- public abstract Collection<ProjectResourceItem> getResources(ResourceType type,
- ProjectResources projectResources);
-
- /**
* Returns the value of a resource generated by this file by {@link ResourceType} and name.
* <p/>If no resource match, <code>null</code> is returned.
* @param type the type of the resource.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java b/ide_common/src/com/android/ide/common/resources/ResourceFolder.java
index b75f226..abdf200 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java
+++ b/ide_common/src/com/android/ide/common/resources/ResourceFolder.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,31 +14,33 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.manager;
+package com.android.ide.common.resources;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.io.IFileWrapper;
+import com.android.annotations.VisibleForTesting;
+import com.android.annotations.VisibleForTesting.Visibility;
+import com.android.ide.common.resources.configuration.Configurable;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.IAbstractFolder;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IFolder;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
/**
* Resource Folder class. Contains list of {@link ResourceFile}s,
- * the {@link FolderConfiguration}, and a link to the workspace {@link IFolder} object.
+ * the {@link FolderConfiguration}, and a link to the {@link IAbstractFolder} object.
*/
-public final class ResourceFolder extends Resource {
- ResourceFolderType mType;
- FolderConfiguration mConfiguration;
+public final class ResourceFolder implements Configurable {
+ final ResourceFolderType mType;
+ final FolderConfiguration mConfiguration;
IAbstractFolder mFolder;
ArrayList<ResourceFile> mFiles = null;
- private final boolean mIsFramework;
+ private final ResourceRepository mRepository;
+
/**
* Creates a new {@link ResourceFolder}
@@ -47,18 +49,65 @@ public final class ResourceFolder extends Resource {
* @param folder The associated {@link IAbstractFolder} object.
* @param isFrameworkRepository
*/
- public ResourceFolder(ResourceFolderType type, FolderConfiguration config,
- IAbstractFolder folder, boolean isFrameworkRepository) {
+ protected ResourceFolder(ResourceFolderType type, FolderConfiguration config,
+ IAbstractFolder folder, ResourceRepository repository) {
mType = type;
mConfiguration = config;
mFolder = folder;
- mIsFramework = isFrameworkRepository;
+ mRepository = repository;
}
/**
+ * Processes a file and adds it to its parent folder resource.
+ * @param file the underlying resource file.
+ * @param folder the parent of the resource file.
+ * @param kind the file change kind.
+ * @return the {@link ResourceFile} that was created.
+ */
+ public ResourceFile processFile(IAbstractFile file, ResourceDeltaKind kind) {
+ // look for this file if it's already been created
+ ResourceFile resFile = getFile(file);
+
+ if (resFile == null) {
+ if (kind != ResourceDeltaKind.REMOVED) {
+ // 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
+ // the ability to have 2 files in the same folder generating 2 different types of
+ // 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.
+ List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(mType);
+
+ if (types.size() == 1) {
+ resFile = new SingleResourceFile(file, this);
+ } else {
+ resFile = new MultiResourceFile(file, this);
+ }
+
+ resFile.load();
+
+ // add it to the folder
+ addFile(resFile);
+ }
+ } else {
+ if (kind == ResourceDeltaKind.REMOVED) {
+ removeFile(resFile);
+ } else {
+ resFile.update();
+ }
+ }
+
+ return resFile;
+ }
+
+
+ /**
* Adds a {@link ResourceFile} to the folder.
* @param file The {@link ResourceFile}.
*/
+ @VisibleForTesting(visibility=Visibility.PROTECTED)
public void addFile(ResourceFile file) {
if (mFiles == null) {
mFiles = new ArrayList<ResourceFile>();
@@ -67,35 +116,21 @@ public final class ResourceFolder extends Resource {
mFiles.add(file);
}
- /**
- * Attempts to remove the {@link ResourceFile} associated with a specified {@link IFile}.
- * @param file the IFile object.
- * @return the {@link ResourceFile} that was removed.
- */
- public ResourceFile removeFile(IFile file) {
- if (mFiles != null) {
- int count = mFiles.size();
- for (int i = 0 ; i < count ; i++) {
- ResourceFile resFile = mFiles.get(i);
- if (resFile != null) {
- IAbstractFile abstractFile = resFile.getFile();
- if (abstractFile instanceof IFileWrapper) {
- IFile iFile = ((IFileWrapper)resFile.getFile()).getIFile();
- if (iFile != null && iFile.equals(file)) {
- mFiles.remove(i);
- touch();
- return resFile;
- }
- }
- }
- }
+ protected void removeFile(ResourceFile file) {
+ file.dispose();
+ mFiles.remove(file);
+ }
+
+ protected void dispose() {
+ for (ResourceFile file : mFiles) {
+ file.dispose();
}
- return null;
+ mFiles.clear();
}
/**
- * Returns the {@link IFolder} associated with this object.
+ * Returns the {@link IAbstractFolder} associated with this object.
*/
public IAbstractFolder getFolder() {
return mFolder;
@@ -108,11 +143,8 @@ public final class ResourceFolder extends Resource {
return mType;
}
- /**
- * Returns whether the folder is a framework resource folder.
- */
- public boolean isFramework() {
- return mIsFramework;
+ public ResourceRepository getRepository() {
+ return mRepository;
}
/**
@@ -123,15 +155,13 @@ public final class ResourceFolder extends Resource {
if (mFiles != null) {
for (ResourceFile file : mFiles) {
- ResourceType[] types = file.getResourceTypes();
+ Collection<ResourceType> types = file.getResourceTypes();
// loop through those and add them to the main list,
// if they are not already present
- if (types != null) {
- for (ResourceType resType : types) {
- if (list.indexOf(resType) == -1) {
- list.add(resType);
- }
+ for (ResourceType resType : types) {
+ if (list.indexOf(resType) == -1) {
+ list.add(resType);
}
}
}
@@ -140,11 +170,6 @@ public final class ResourceFolder extends Resource {
return list;
}
- /*
- * (non-Javadoc)
- * @see com.android.ide.eclipse.editors.resources.manager.Resource#getConfiguration()
- */
- @Override
public FolderConfiguration getConfiguration() {
return mConfiguration;
}
@@ -159,10 +184,10 @@ public final class ResourceFolder extends Resource {
/**
* Returns the {@link ResourceFile} matching a {@link IAbstractFile} object.
- * @param file The {@link IFile} object.
+ * @param file The {@link IAbstractFile} object.
* @return the {@link ResourceFile} or null if no match was found.
*/
- public ResourceFile getFile(IAbstractFile file) {
+ private ResourceFile getFile(IAbstractFile file) {
if (mFiles != null) {
for (ResourceFile f : mFiles) {
if (f.getFile().equals(file)) {
@@ -174,27 +199,6 @@ public final class ResourceFolder extends Resource {
}
/**
- * Returns the {@link ResourceFile} matching a {@link IFile} object.
- * @param file The {@link IFile} object.
- * @return the {@link ResourceFile} or null if no match was found.
- */
- public ResourceFile getFile(IFile file) {
- if (mFiles != null) {
- for (ResourceFile f : mFiles) {
- IAbstractFile abstractFile = f.getFile();
- if (abstractFile instanceof IFileWrapper) {
- IFile iFile = ((IFileWrapper)f.getFile()).getIFile();
- if (iFile != null && iFile.equals(file)) {
- return f;
- }
- }
- }
- }
- return null;
- }
-
-
- /**
* Returns the {@link ResourceFile} matching a given name.
* @param filename The name of the file to return.
* @return the {@link ResourceFile} or <code>null</code> if no match was found.
@@ -217,7 +221,7 @@ public final class ResourceFolder extends Resource {
public boolean hasResources(ResourceType type) {
// Check if the folder type is able to generate resource of the type that was asked.
// this is a first check to avoid going through the files.
- ResourceFolderType[] folderTypes = FolderTypeRelationship.getRelatedFolders(type);
+ List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
boolean valid = false;
for (ResourceFolderType rft : folderTypes) {
@@ -239,27 +243,6 @@ public final class ResourceFolder extends Resource {
return false;
}
- /**
- * Get the list of {@link ResourceItem} of a specific type generated by all the files
- * in the folder.
- * This method must make sure not to create duplicates.
- * @param type The type of {@link ResourceItem} to return.
- * @param projectResources The global Project Resource object, allowing the implementation to
- * query for already existing {@link ResourceItem}
- * @return The list of <b>new</b> {@link ResourceItem}
- * @see ProjectResources#findResourceItem(ResourceType, String)
- */
- public Collection<ProjectResourceItem> getResources(ResourceType type,
- ProjectResources projectResources) {
- Collection<ProjectResourceItem> list = new ArrayList<ProjectResourceItem>();
- if (mFiles != null) {
- for (ResourceFile f : mFiles) {
- list.addAll(f.getResources(type, projectResources));
- }
- }
- return list;
- }
-
@Override
public String toString() {
return mFolder.toString();
diff --git a/ide_common/src/com/android/ide/common/resources/ResourceItem.java b/ide_common/src/com/android/ide/common/resources/ResourceItem.java
new file mode 100644
index 0000000..dd28a9a
--- /dev/null
+++ b/ide_common/src/com/android/ide/common/resources/ResourceItem.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2011 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.ide.common.resources;
+
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.resources.ResourceType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An android resource.
+ *
+ * This is a representation of the resource, not of its value(s). It gives access to all
+ * the source files that generate this particular resource which then can be used to access
+ * the actual value(s).
+ *
+ * @see ResourceFile#getResources(ResourceType, ResourceRepository)
+ */
+public class ResourceItem implements Comparable<ResourceItem> {
+
+ private final static Comparator<ResourceFile> sComparator = new Comparator<ResourceFile>() {
+ public int compare(ResourceFile file1, ResourceFile file2) {
+ // get both FolderConfiguration and compare them
+ FolderConfiguration fc1 = file1.getFolder().getConfiguration();
+ FolderConfiguration fc2 = file2.getFolder().getConfiguration();
+
+ return fc1.compareTo(fc2);
+ }
+ };
+
+ private final String mName;
+
+ /**
+ * List of files generating this ResourceItem.
+ */
+ private final List<ResourceFile> mFiles = new ArrayList<ResourceFile>();
+
+ /**
+ * Constructs a new ResourceItem.
+ * @param name the name of the resource as it appears in the XML and R.java files.
+ */
+ public ResourceItem(String name) {
+ mName = name;
+ }
+
+ /**
+ * Returns the name of the resource.
+ */
+ public final String getName() {
+ return mName;
+ }
+
+ /**
+ * Compares the {@link ResourceItem} to another.
+ * @param other the ResourceItem to be compared to.
+ */
+ public int compareTo(ResourceItem other) {
+ return mName.compareTo(other.mName);
+ }
+
+ /**
+ * Returns whether the resource is editable directly.
+ * <p/>
+ * This is typically the case for resources that don't have alternate versions, or resources
+ * of type {@link ResourceType#ID} that aren't declared inline.
+ */
+ public boolean isEditableDirectly() {
+ return hasAlternates() == false;
+ }
+
+ /**
+ * Returns whether the ID resource has been declared inline inside another resource XML file.
+ * If the resource type is not {@link ResourceType#ID}, this will always return {@code false}.
+ */
+ public boolean isDeclaredInline() {
+ return false;
+ }
+
+ /**
+ * Returns a {@link ResourceValue} for this item based on the given configuration.
+ * If the ResourceItem has several source files, one will be selected based on the config.
+ * @param type the type of the resource. This is necessary because ResourceItem doesn't embed
+ * its type, but ResourceValue does.
+ * @param referenceConfig the config of the resource item.
+ * @param isFramework whether the resource is a framework value. Same as the type.
+ * @return a ResourceValue or null if none match the config.
+ */
+ public ResourceValue getResourceValue(ResourceType type, FolderConfiguration referenceConfig,
+ boolean isFramework) {
+ // look for the best match for the given configuration
+ // the match has to be of type ResourceFile since that's what the input list contains
+ ResourceFile match = (ResourceFile) referenceConfig.findMatchingConfigurable(mFiles);
+
+ if (match != null) {
+ // get the value of this configured resource.
+ return match.getValue(type, mName);
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds a new source file.
+ * @param file the source file.
+ */
+ protected void add(ResourceFile file) {
+ mFiles.add(file);
+ }
+
+ /**
+ * Removes a file from the list of source files.
+ * @param file the file to remove
+ */
+ protected void removeFile(ResourceFile file) {
+ mFiles.remove(file);
+ }
+
+ /**
+ * Returns {@code true} if the item has no source file.
+ * @return
+ */
+ protected boolean hasNoSourceFile() {
+ return mFiles.size() == 0;
+ }
+
+ /**
+ * Reset the item by emptying its source file list.
+ */
+ protected void reset() {
+ mFiles.clear();
+ }
+
+ /**
+ * Returns the sorted list of {@link ResourceItem} objects for this resource item.
+ */
+ public ResourceFile[] getSourceFileArray() {
+ ArrayList<ResourceFile> list = new ArrayList<ResourceFile>();
+ list.addAll(mFiles);
+
+ Collections.sort(list, sComparator);
+
+ return list.toArray(new ResourceFile[list.size()]);
+ }
+
+ /**
+ * Returns the list of source file for this resource.
+ */
+ public List<ResourceFile> getSourceFileList() {
+ return Collections.unmodifiableList(mFiles);
+ }
+
+ /**
+ * Returns if the resource has at least one non-default version.
+ *
+ * @see ResourceFile#getConfiguration()
+ * @see FolderConfiguration#isDefault()
+ */
+ public boolean hasAlternates() {
+ for (ResourceFile file : mFiles) {
+ if (file.getFolder().getConfiguration().isDefault() == false) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the resource has a default version, with no qualifier.
+ *
+ * @see ResourceFile#getConfiguration()
+ * @see FolderConfiguration#isDefault()
+ */
+ public boolean hasDefault() {
+ for (ResourceFile file : mFiles) {
+ if (file.getFolder().getConfiguration().isDefault()) {
+ return true;
+ }
+ }
+
+ // We only want to return false if there's no default and more than 0 items.
+ return (mFiles.size() == 0);
+ }
+
+ /**
+ * Returns the number of alternate versions for this resource.
+ *
+ * @see ResourceFile#getConfiguration()
+ * @see FolderConfiguration#isDefault()
+ */
+ public int getAlternateCount() {
+ int count = 0;
+ for (ResourceFile file : mFiles) {
+ if (file.getFolder().getConfiguration().isDefault() == false) {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ /**
+ * Returns a formatted string usable in an XML to use for the {@link ResourceItem}.
+ * @param system Whether this is a system resource or a project resource.
+ * @return a string in the format @[type]/[name]
+ */
+ public String getXmlString(ResourceType type, boolean system) {
+ if (type == ResourceType.ID && isDeclaredInline()) {
+ return (system ? "@android:" : "@+") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ return (system ? "@android:" : "@") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceItem [mName=" + mName + ", mFiles=" + mFiles + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+}
diff --git a/ide_common/src/com/android/ide/common/resources/ResourceRepository.java b/ide_common/src/com/android/ide/common/resources/ResourceRepository.java
new file mode 100644
index 0000000..41e4f89
--- /dev/null
+++ b/ide_common/src/com/android/ide/common/resources/ResourceRepository.java
@@ -0,0 +1,546 @@
+/*
+ * 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.
+ */
+
+package com.android.ide.common.resources;
+
+import com.android.AndroidConstants;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.configuration.Configurable;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.LanguageQualifier;
+import com.android.ide.common.resources.configuration.RegionQualifier;
+import com.android.io.IAbstractFolder;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Base class for resource repository.
+ *
+ * A repository is both a file representation of a resource folder and a representation
+ * of the generated resources, organized by type.
+ *
+ * {@link #getResourceFolder(IAbstractFolder)} and {@link #getSourceFiles(ResourceType, String, FolderConfiguration)}
+ * give access to the folders and files of the resource folder.
+ *
+ * {@link #getResources(ResourceType)} gives access to the resources directly.
+ *
+ */
+public abstract class ResourceRepository {
+
+ protected final Map<ResourceFolderType, List<ResourceFolder>> mFolderMap =
+ new EnumMap<ResourceFolderType, List<ResourceFolder>>(ResourceFolderType.class);
+
+ protected final Map<ResourceType, List<ResourceItem>> mResourceMap =
+ new EnumMap<ResourceType, List<ResourceItem>>(ResourceType.class);
+
+ private final Map<List<ResourceItem>, List<ResourceItem>> mReadOnlyListMap =
+ new IdentityHashMap<List<ResourceItem>, List<ResourceItem>>();
+
+ private final boolean mFrameworkRepository;
+
+ protected final IntArrayWrapper mWrapper = new IntArrayWrapper(null);
+
+
+ /**
+ * Makes a resource repository
+ * @param isFrameworkRepository whether the repository is for framework resources.
+ */
+ protected ResourceRepository(boolean isFrameworkRepository) {
+ mFrameworkRepository = isFrameworkRepository;
+ }
+
+ public boolean isFrameworkRepository() {
+ return mFrameworkRepository;
+ }
+
+ /**
+ * Adds a Folder Configuration to the project.
+ * @param type The resource type.
+ * @param config The resource configuration.
+ * @param folder The workspace folder object.
+ * @return the {@link ResourceFolder} object associated to this folder.
+ */
+ private ResourceFolder add(ResourceFolderType type, FolderConfiguration config,
+ IAbstractFolder folder) {
+ // get the list for the resource type
+ List<ResourceFolder> list = mFolderMap.get(type);
+
+ if (list == null) {
+ list = new ArrayList<ResourceFolder>();
+
+ ResourceFolder cf = new ResourceFolder(type, config, folder, this);
+ list.add(cf);
+
+ mFolderMap.put(type, list);
+
+ return cf;
+ }
+
+ // look for an already existing folder configuration.
+ for (ResourceFolder cFolder : list) {
+ if (cFolder.mConfiguration.equals(config)) {
+ // config already exist. Nothing to be done really, besides making sure
+ // the IAbstractFolder object is up to date.
+ cFolder.mFolder = folder;
+ return cFolder;
+ }
+ }
+
+ // If we arrive here, this means we didn't find a matching configuration.
+ // So we add one.
+ ResourceFolder cf = new ResourceFolder(type, config, folder, this);
+ list.add(cf);
+
+ return cf;
+ }
+
+ /**
+ * Removes a {@link ResourceFolder} associated with the specified {@link IAbstractFolder}.
+ * @param type The type of the folder
+ * @param removedFolder the IAbstractFolder object.
+ * @return the {@link ResourceFolder} that was removed, or null if no matches were found.
+ */
+ public ResourceFolder removeFolder(ResourceFolderType type, IAbstractFolder removedFolder) {
+ // get the list of folders for the resource type.
+ List<ResourceFolder> list = mFolderMap.get(type);
+
+ if (list != null) {
+ int count = list.size();
+ for (int i = 0 ; i < count ; i++) {
+ ResourceFolder resFolder = list.get(i);
+ IAbstractFolder folder = resFolder.getFolder();
+ if (removedFolder.equals(folder)) {
+ // we found the matching ResourceFolder. we need to remove it.
+ list.remove(i);
+
+ // remove its content
+ resFolder.dispose();
+
+ return resFolder;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a {@link ResourceItem} matching the given {@link ResourceType} and name. If none
+ * exist, it creates one.
+ *
+ * @param type the resource type
+ * @param name the name of the resource.
+ * @return A resource item matching the type and name.
+ */
+ protected ResourceItem getResourceItem(ResourceType type, String name) {
+ // looking for an existing ResourceItem with this type and name
+ ResourceItem item = findDeclaredResourceItem(type, name);
+
+ // create one if there isn't one already, or if the existing one is inlined, since
+ // clearly we need a non inlined one (the inline one is removed too)
+ if (item == null || item.isDeclaredInline()) {
+ ResourceItem oldItem = item != null && item.isDeclaredInline() ? item : null;
+
+ item = createResourceItem(name);
+
+ List<ResourceItem> list = mResourceMap.get(type);
+ if (list == null) {
+ list = new ArrayList<ResourceItem>();
+ mResourceMap.put(type, list);
+ }
+
+ list.add(item);
+
+ if (oldItem != null) {
+ list.remove(oldItem);
+ }
+ }
+
+ return item;
+ }
+
+ /**
+ * Creates a resource item with the given name.
+ * @param name the name of the resource
+ * @return a new ResourceItem (or child class) instance.
+ */
+ protected abstract ResourceItem createResourceItem(String name);
+
+ /**
+ * Processes a folder and adds it to the list of existing folders.
+ * @param folder the folder to process
+ * @return the ResourceFolder created from this folder, or null if the process failed.
+ */
+ public ResourceFolder processFolder(IAbstractFolder folder) {
+ // split the name of the folder in segments.
+ String[] folderSegments = folder.getName().split(AndroidConstants.RES_QUALIFIER_SEP);
+
+ // get the enum for the resource type.
+ ResourceFolderType type = ResourceFolderType.getTypeByName(folderSegments[0]);
+
+ if (type != null) {
+ // get the folder configuration.
+ FolderConfiguration config = FolderConfiguration.getConfig(folderSegments);
+
+ if (config != null) {
+ return add(type, config, folder);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a list of {@link ResourceFolder} for a specific {@link ResourceFolderType}.
+ * @param type The {@link ResourceFolderType}
+ */
+ public List<ResourceFolder> getFolders(ResourceFolderType type) {
+ return mFolderMap.get(type);
+ }
+
+ public List<ResourceType> getAvailableResourceTypes() {
+ List<ResourceType> list = new ArrayList<ResourceType>();
+
+ // For each key, we check if there's a single ResourceType match.
+ // If not, we look for the actual content to give us the resource type.
+
+ for (ResourceFolderType folderType : mFolderMap.keySet()) {
+ List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(folderType);
+ if (types.size() == 1) {
+ // before we add it we check if it's not already present, since a ResourceType
+ // could be created from multiple folders, even for the folders that only create
+ // one type of resource (drawable for instance, can be created from drawable/ and
+ // values/)
+ if (list.contains(types.get(0)) == false) {
+ list.add(types.get(0));
+ }
+ } else {
+ // there isn't a single resource type out of this folder, so we look for all
+ // content.
+ List<ResourceFolder> folders = mFolderMap.get(folderType);
+ if (folders != null) {
+ for (ResourceFolder folder : folders) {
+ Collection<ResourceType> folderContent = folder.getResourceTypes();
+
+ // then we add them, but only if they aren't already in the list.
+ for (ResourceType folderResType : folderContent) {
+ if (list.contains(folderResType) == false) {
+ list.add(folderResType);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Returns a list of {@link ResourceItem} matching a given {@link ResourceType}.
+ * @param type the type of the resource items to return
+ * @return a non null collection of resource items
+ */
+ public Collection<ResourceItem> getResourceItemsOfType(ResourceType type) {
+ List<ResourceItem> list = mResourceMap.get(type);
+
+ if (list == null) {
+ return Collections.emptyList();
+ }
+
+ List<ResourceItem> roList = mReadOnlyListMap.get(list);
+ if (roList == null) {
+ roList = Collections.unmodifiableList(list);
+ mReadOnlyListMap.put(list, roList);
+ }
+
+ return roList;
+ }
+
+ /**
+ * Returns whether the repository has resources of a given {@link ResourceType}.
+ * @param type the type of resource to check.
+ * @return true if the repository contains resources of the given type, false otherwise.
+ */
+ public boolean hasResourcesOfType(ResourceType type) {
+ List<ResourceItem> items = mResourceMap.get(type);
+ return (items != null && items.size() > 0);
+ }
+
+ /**
+ * Returns the {@link ResourceFolder} associated with a {@link IAbstractFolder}.
+ * @param folder The {@link IAbstractFolder} object.
+ * @return the {@link ResourceFolder} or null if it was not found.
+ */
+ public ResourceFolder getResourceFolder(IAbstractFolder folder) {
+ for (List<ResourceFolder> list : mFolderMap.values()) {
+ for (ResourceFolder resFolder : list) {
+ IAbstractFolder wrapper = resFolder.getFolder();
+ if (wrapper.equals(folder)) {
+ return resFolder;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the {@link ResourceFile} matching the given name, {@link ResourceFolderType} and
+ * configuration.
+ * <p/>This only works with files generating one resource named after the file (for instance,
+ * layouts, bitmap based drawable, xml, anims).
+ * @return the matching file or <code>null</code> if no match was found.
+ */
+ public ResourceFile getMatchingFile(String name, ResourceFolderType type,
+ FolderConfiguration config) {
+ // get the folders for the given type
+ List<ResourceFolder> folders = mFolderMap.get(type);
+
+ // look for folders containing a file with the given name.
+ ArrayList<ResourceFolder> matchingFolders = new ArrayList<ResourceFolder>(folders.size());
+
+ // remove the folders that do not have a file with the given name.
+ for (int i = 0 ; i < folders.size(); i++) {
+ ResourceFolder folder = folders.get(i);
+
+ if (folder.hasFile(name) == true) {
+ matchingFolders.add(folder);
+ }
+ }
+
+ // from those, get the folder with a config matching the given reference configuration.
+ Configurable match = config.findMatchingConfigurable(matchingFolders);
+
+ // do we have a matching folder?
+ if (match instanceof ResourceFolder) {
+ // get the ResourceFile from the filename
+ return ((ResourceFolder)match).getFile(name);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the list of source files for a given resource.
+ * Optionally, if a {@link FolderConfiguration} is given, then only the best
+ * match for this config is returned.
+ *
+ * @param type the type of the resource.
+ * @param name the name of the resource.
+ * @param referenceConfig an optional config for which only the best match will be returned.
+ *
+ * @return a list of files generating this resource or null if it was not found.
+ */
+ public List<ResourceFile> getSourceFiles(ResourceType type, String name,
+ FolderConfiguration referenceConfig) {
+
+ Collection<ResourceItem> items = getResourceItemsOfType(type);
+
+ for (ResourceItem item : items) {
+ if (name.equals(item.getName())) {
+ if (referenceConfig != null) {
+ Configurable match = referenceConfig.findMatchingConfigurable(
+ item.getSourceFileList());
+
+ if (match instanceof ResourceFile) {
+ return Collections.singletonList((ResourceFile) match);
+ }
+
+ return null;
+ }
+ return item.getSourceFileList();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the resources values matching a given {@link FolderConfiguration}.
+ *
+ * @param referenceConfig the configuration that each value must match.
+ * @return a map with guaranteed to contain an entry for each {@link ResourceType}
+ */
+ public Map<ResourceType, Map<String, ResourceValue>> getConfiguredResources(
+ FolderConfiguration referenceConfig) {
+ return doGetConfiguredResources(referenceConfig);
+ }
+
+ /**
+ * Returns the resources values matching a given {@link FolderConfiguration} for the current
+ * project.
+ *
+ * @param referenceConfig the configuration that each value must match.
+ * @return a map with guaranteed to contain an entry for each {@link ResourceType}
+ */
+ protected final Map<ResourceType, Map<String, ResourceValue>> doGetConfiguredResources(
+ FolderConfiguration referenceConfig) {
+
+ Map<ResourceType, Map<String, ResourceValue>> map =
+ new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class);
+
+ for (ResourceType key : ResourceType.values()) {
+ // get the local results and put them in the map
+ map.put(key, getConfiguredResource(key, referenceConfig));
+ }
+
+ return map;
+ }
+
+ /**
+ * Returns the sorted list of languages used in the resources.
+ */
+ public SortedSet<String> getLanguages() {
+ SortedSet<String> set = new TreeSet<String>();
+
+ Collection<List<ResourceFolder>> folderList = mFolderMap.values();
+ for (List<ResourceFolder> folderSubList : folderList) {
+ for (ResourceFolder folder : folderSubList) {
+ FolderConfiguration config = folder.getConfiguration();
+ LanguageQualifier lang = config.getLanguageQualifier();
+ if (lang != null) {
+ set.add(lang.getShortDisplayValue());
+ }
+ }
+ }
+
+ return set;
+ }
+
+ /**
+ * Returns the sorted list of regions used in the resources with the given language.
+ * @param currentLanguage the current language the region must be associated with.
+ */
+ public SortedSet<String> getRegions(String currentLanguage) {
+ SortedSet<String> set = new TreeSet<String>();
+
+ Collection<List<ResourceFolder>> folderList = mFolderMap.values();
+ for (List<ResourceFolder> folderSubList : folderList) {
+ for (ResourceFolder folder : folderSubList) {
+ FolderConfiguration config = folder.getConfiguration();
+
+ // get the language
+ LanguageQualifier lang = config.getLanguageQualifier();
+ if (lang != null && lang.getShortDisplayValue().equals(currentLanguage)) {
+ RegionQualifier region = config.getRegionQualifier();
+ if (region != null) {
+ set.add(region.getShortDisplayValue());
+ }
+ }
+ }
+ }
+
+ return set;
+ }
+
+ protected void removeFile(Collection<ResourceType> types, ResourceFile file) {
+ for (ResourceType type : types) {
+ removeFile(type, file);
+ }
+ }
+
+ protected void removeFile(ResourceType type, ResourceFile file) {
+ List<ResourceItem> list = mResourceMap.get(type);
+ for (int i = 0 ; i < list.size(); i++) {
+ ResourceItem item = list.get(i);
+ item.removeFile(file);
+ }
+ }
+
+ /**
+ * Returns a map of (resource name, resource value) for the given {@link ResourceType}.
+ * <p/>The values returned are taken from the resource files best matching a given
+ * {@link FolderConfiguration}.
+ * @param type the type of the resources.
+ * @param referenceConfig the configuration to best match.
+ */
+ private Map<String, ResourceValue> getConfiguredResource(ResourceType type,
+ FolderConfiguration referenceConfig) {
+ // get the resource item for the given type
+ List<ResourceItem> items = mResourceMap.get(type);
+ if (items == null) {
+ return Collections.emptyMap();
+ }
+
+ // create the map
+ HashMap<String, ResourceValue> map = new HashMap<String, ResourceValue>(items.size());
+
+ for (ResourceItem item : items) {
+ ResourceValue value = item.getResourceValue(type, referenceConfig,
+ isFrameworkRepository());
+ if (value != null) {
+ map.put(item.getName(), value);
+ }
+ }
+
+ return map;
+ }
+
+
+ /**
+ * Called after a resource change event, when the resource delta has been processed.
+ */
+ protected void postUpdate() {
+ // Since removed files/folders remove source files from existing ResourceItem, loop through
+ // all resource items and remove the ones that have no source files.
+
+ Collection<List<ResourceItem>> lists = mResourceMap.values();
+ for (List<ResourceItem> list : lists) {
+ for (int i = 0 ; i < list.size() ;) {
+ if (list.get(i).hasNoSourceFile()) {
+ list.remove(i);
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+
+ /**
+ * Looks up an existing {@link ResourceItem} by {@link ResourceType} and name. This
+ * ignores inline resources.
+ * @param type the Resource Type.
+ * @param name the Resource name.
+ * @return the existing ResourceItem or null if no match was found.
+ */
+ private ResourceItem findDeclaredResourceItem(ResourceType type, String name) {
+ List<ResourceItem> list = mResourceMap.get(type);
+
+ if (list != null) {
+ for (ResourceItem item : list) {
+ // ignore inline
+ if (name.equals(item.getName()) && item.isDeclaredInline() == false) {
+ return item;
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/ide_common/src/com/android/ide/common/resources/ResourceResolver.java b/ide_common/src/com/android/ide/common/resources/ResourceResolver.java
index 3948ac5..89a4cba 100644
--- a/ide_common/src/com/android/ide/common/resources/ResourceResolver.java
+++ b/ide_common/src/com/android/ide/common/resources/ResourceResolver.java
@@ -28,13 +28,23 @@ import java.util.Map;
public class ResourceResolver extends RenderResources {
- private final static String REFERENCE_STYLE = ResourceType.STYLE.getName() + "/";
- private final static String PREFIX_ANDROID_RESOURCE_REF = "@android:";
- private final static String PREFIX_RESOURCE_REF = "@";
- private final static String PREFIX_ANDROID_THEME_REF = "?android:";
- private final static String PREFIX_THEME_REF = "?";
- private final static String PREFIX_ANDROID = "android:";
-
+ /** The constant {@code style/} */
+ public final static String REFERENCE_STYLE = ResourceType.STYLE.getName() + "/";
+ /** The constant {@code @android:} */
+ public final static String PREFIX_ANDROID_RESOURCE_REF = "@android:";
+ /** The constant {@code @} */
+ public final static String PREFIX_RESOURCE_REF = "@";
+ /** The constant {@code ?android:} */
+ public final static String PREFIX_ANDROID_THEME_REF = "?android:";
+ /** The constant {@code ?} */
+ public final static String PREFIX_THEME_REF = "?";
+ /** The constant {@code android:} */
+ public final static String PREFIX_ANDROID = "android:";
+ /** The constant {@code @style/} */
+ public static final String PREFIX_STYLE = PREFIX_RESOURCE_REF + REFERENCE_STYLE;
+ /** The constant {@code @android:style/} */
+ public static final String PREFIX_ANDROID_STYLE = PREFIX_ANDROID_RESOURCE_REF
+ + REFERENCE_STYLE;
private final Map<ResourceType, Map<String, ResourceValue>> mProjectResources;
private final Map<ResourceType, Map<String, ResourceValue>> mFrameworkResources;
@@ -121,14 +131,10 @@ public class ResourceResolver extends RenderResources {
if (frameworkTheme) {
Map<String, ResourceValue> frameworkStyleMap = mFrameworkResources.get(
ResourceType.STYLE);
- if (frameworkStyleMap != null) {
- theme = frameworkStyleMap.get(name);
- }
+ theme = frameworkStyleMap.get(name);
} else {
Map<String, ResourceValue> projectStyleMap = mProjectResources.get(ResourceType.STYLE);
- if (projectStyleMap != null) {
- theme = projectStyleMap.get(name);
- }
+ theme = projectStyleMap.get(name);
}
if (theme instanceof StyleResourceValue) {
@@ -334,29 +340,25 @@ public class ResourceResolver extends RenderResources {
// if allowed, search in the project resources first.
if (frameworkOnly == false) {
typeMap = mProjectResources.get(resType);
- if (typeMap != null) {
- ResourceValue item = typeMap.get(resName);
- if (item != null) {
- return item;
- }
+ ResourceValue item = typeMap.get(resName);
+ if (item != null) {
+ return item;
}
}
// now search in the framework resources.
typeMap = mFrameworkResources.get(resType);
- if (typeMap != null) {
- ResourceValue item = typeMap.get(resName);
- if (item != null) {
- return item;
- }
+ ResourceValue item = typeMap.get(resName);
+ if (item != null) {
+ return item;
+ }
- // if it was not found and the type is an id, it is possible that the ID was
- // generated dynamically when compiling the framework resources.
- // Look for it in the R map.
- if (mFrameworkProvider != null && resType == ResourceType.ID) {
- if (mFrameworkProvider.getId(resType, resName) != null) {
- return new ResourceValue(resType, resName, true);
- }
+ // if it was not found and the type is an id, it is possible that the ID was
+ // generated dynamically when compiling the framework resources.
+ // Look for it in the R map.
+ if (mFrameworkProvider != null && resType == ResourceType.ID) {
+ if (mFrameworkProvider.getId(resType, resName) != null) {
+ return new ResourceValue(resType, resName, true);
}
}
@@ -397,32 +399,30 @@ public class ResourceResolver extends RenderResources {
Map<String, ResourceValue> projectStyleMap = mProjectResources.get(ResourceType.STYLE);
Map<String, ResourceValue> frameworkStyleMap = mFrameworkResources.get(ResourceType.STYLE);
- if (projectStyleMap != null && frameworkStyleMap != null) {
- // first, get the theme
- ResourceValue theme = null;
-
- // project theme names have been prepended with a *
- if (isProjectTheme) {
- theme = projectStyleMap.get(themeName);
- } else {
- theme = frameworkStyleMap.get(themeName);
- }
-
- if (theme instanceof StyleResourceValue) {
- // compute the inheritance map for both the project and framework styles
- computeStyleInheritance(projectStyleMap.values(), projectStyleMap,
- frameworkStyleMap);
+ // first, get the theme
+ ResourceValue theme = null;
- // Compute the style inheritance for the framework styles/themes.
- // Since, for those, the style parent values do not contain 'android:'
- // we want to force looking in the framework style only to avoid using
- // similarly named styles from the project.
- // To do this, we pass null in lieu of the project style map.
- computeStyleInheritance(frameworkStyleMap.values(), null /*inProjectStyleMap */,
- frameworkStyleMap);
+ // project theme names have been prepended with a *
+ if (isProjectTheme) {
+ theme = projectStyleMap.get(themeName);
+ } else {
+ theme = frameworkStyleMap.get(themeName);
+ }
- mTheme = (StyleResourceValue) theme;
- }
+ if (theme instanceof StyleResourceValue) {
+ // compute the inheritance map for both the project and framework styles
+ computeStyleInheritance(projectStyleMap.values(), projectStyleMap,
+ frameworkStyleMap);
+
+ // Compute the style inheritance for the framework styles/themes.
+ // Since, for those, the style parent values do not contain 'android:'
+ // we want to force looking in the framework style only to avoid using
+ // similarly named styles from the project.
+ // To do this, we pass null in lieu of the project style map.
+ computeStyleInheritance(frameworkStyleMap.values(), null /*inProjectStyleMap */,
+ frameworkStyleMap);
+
+ mTheme = (StyleResourceValue) theme;
}
}
@@ -537,7 +537,6 @@ public class ResourceResolver extends RenderResources {
null /*data*/);
}
- assert false;
return null;
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java b/ide_common/src/com/android/ide/common/resources/SingleResourceFile.java
index 953e57a..cd2b627 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java
+++ b/ide_common/src/com/android/ide/common/resources/SingleResourceFile.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,18 +14,17 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.manager;
+package com.android.ide.common.resources;
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
+import com.android.ide.common.resources.configuration.PixelDensityQualifier;
+import com.android.io.IAbstractFile;
+import com.android.resources.FolderTypeRelationship;
import com.android.resources.ResourceType;
-import com.android.sdklib.io.IAbstractFile;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.List;
import javax.xml.parsers.SAXParserFactory;
@@ -42,16 +41,6 @@ public class SingleResourceFile extends ResourceFile {
sParserFactory.setNamespaceAware(true);
}
- private final static Pattern sXmlPattern = Pattern.compile("^(.+)\\.xml", //$NON-NLS-1$
- Pattern.CASE_INSENSITIVE);
-
- private final static Pattern[] sDrawablePattern = new Pattern[] {
- Pattern.compile("^(.+)\\.9\\.png", Pattern.CASE_INSENSITIVE), //$NON-NLS-1$
- Pattern.compile("^(.+)\\.png", Pattern.CASE_INSENSITIVE), //$NON-NLS-1$
- Pattern.compile("^(.+)\\.jpg", Pattern.CASE_INSENSITIVE), //$NON-NLS-1$
- Pattern.compile("^(.+)\\.gif", Pattern.CASE_INSENSITIVE), //$NON-NLS-1$
- };
-
private String mResourceName;
private ResourceType mType;
private ResourceValue mValue;
@@ -61,8 +50,8 @@ public class SingleResourceFile extends ResourceFile {
// we need to infer the type of the resource from the folder type.
// This is easy since this is a single Resource file.
- ResourceType[] types = FolderTypeRelationship.getRelatedResourceTypes(folder.getType());
- mType = types[0];
+ List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(folder.getType());
+ mType = types.get(0);
// compute the resource name
mResourceName = getResourceName(mType);
@@ -84,33 +73,37 @@ public class SingleResourceFile extends ResourceFile {
}
@Override
- public ResourceType[] getResourceTypes() {
- return FolderTypeRelationship.getRelatedResourceTypes(getFolder().getType());
+ protected void load() {
+ // get a resource item matching the given type and name
+ ResourceItem item = getRepository().getResourceItem(mType, mResourceName);
+
+ // add this file to the list of files generating this resource item.
+ item.add(this);
}
@Override
- public boolean hasResources(ResourceType type) {
- return FolderTypeRelationship.match(type, getFolder().getType());
+ protected void update() {
+ // when this happens, nothing needs to be done since the file only generates
+ // a single resources that doesn't actually change (its content is the file path)
}
@Override
- public Collection<ProjectResourceItem> getResources(ResourceType type,
- ProjectResources projectResources) {
-
- // looking for an existing ResourceItem with this name and type
- ProjectResourceItem item = projectResources.findResourceItem(type, mResourceName);
+ protected void dispose() {
+ // only remove this file from the existing ResourceItem.
+ getFolder().getRepository().removeFile(mType, this);
- ArrayList<ProjectResourceItem> items = new ArrayList<ProjectResourceItem>();
-
- if (item == null) {
- item = new ConfigurableResourceItem(mResourceName);
- items.add(item);
- }
+ // don't need to touch the content, it'll get reclaimed as this objects disappear.
+ // In the mean time other objects may need to access it.
+ }
- // add this ResourceFile to the ResourceItem
- item.add(this);
+ @Override
+ public Collection<ResourceType> getResourceTypes() {
+ return FolderTypeRelationship.getRelatedResourceTypes(getFolder().getType());
+ }
- return items;
+ @Override
+ public boolean hasResources(ResourceType type) {
+ return FolderTypeRelationship.match(type, getFolder().getType());
}
/*
@@ -133,31 +126,11 @@ public class SingleResourceFile extends ResourceFile {
// get the name from the filename.
String name = getFile().getName();
- if (type == ResourceType.ANIM ||
- type == ResourceType.ANIMATOR ||
- type == ResourceType.COLOR ||
- type == ResourceType.INTERPOLATOR ||
- type == ResourceType.LAYOUT ||
- type == ResourceType.MENU ||
- type == ResourceType.XML) {
- Matcher m = sXmlPattern.matcher(name);
- if (m.matches()) {
- return m.group(1);
- }
- } else if (type == ResourceType.DRAWABLE) {
- for (Pattern p : sDrawablePattern) {
- Matcher m = p.matcher(name);
- if (m.matches()) {
- return m.group(1);
- }
- }
-
- // also try the Xml pattern for selector/shape based drawable.
- Matcher m = sXmlPattern.matcher(name);
- if (m.matches()) {
- return m.group(1);
- }
+ int pos = name.indexOf('.');
+ if (pos != -1) {
+ name = name.substring(0, pos);
}
+
return name;
}
}
diff --git a/ide_common/src/com/android/ide/common/resources/configuration/Configurable.java b/ide_common/src/com/android/ide/common/resources/configuration/Configurable.java
new file mode 100644
index 0000000..5e7f910
--- /dev/null
+++ b/ide_common/src/com/android/ide/common/resources/configuration/Configurable.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package com.android.ide.common.resources.configuration;
+
+
+/**
+ * An object that is associated with a {@link FolderConfiguration}.
+ */
+public interface Configurable {
+ /**
+ * Returns the {@link FolderConfiguration} for this object.
+ */
+ public FolderConfiguration getConfiguration();
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/CountryCodeQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/CountryCodeQualifier.java
index fcad2dc..7195ba5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/CountryCodeQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/CountryCodeQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-
-import org.eclipse.swt.graphics.Image;
+package com.android.ide.common.resources.configuration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -98,11 +94,6 @@ public final class CountryCodeQualifier extends ResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("mcc"); //$NON-NLS-1$
- }
-
- @Override
public boolean isValid() {
return mCode != DEFAULT_CODE;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/DockModeQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/DockModeQualifier.java
index 37af3b4..2c832eb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/DockModeQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/DockModeQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.DockMode;
import com.android.resources.ResourceEnum;
-import org.eclipse.swt.graphics.Image;
-
/**
* Resource Qualifier for Navigation Method.
*/
@@ -58,12 +55,6 @@ public final class DockModeQualifier extends EnumBasedResourceQualifier {
return "Dock Mode";
}
-
- @Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("dockmode"); //$NON-NLS-1$
- }
-
@Override
public boolean checkAndSet(String value, FolderConfiguration config) {
DockMode mode = DockMode.getEnum(value);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/EnumBasedResourceQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/EnumBasedResourceQualifier.java
index 9526add..7bfda2d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/EnumBasedResourceQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/EnumBasedResourceQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
import com.android.resources.ResourceEnum;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java b/ide_common/src/com/android/ide/common/resources/configuration/FolderConfiguration.java
index b759e6e..09cf9e4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/FolderConfiguration.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,9 +14,13 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
+import com.android.AndroidConstants;
+import com.android.resources.ResourceFolderType;
+
+import java.util.ArrayList;
+import java.util.List;
/**
@@ -24,7 +28,16 @@ import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType
* value which means that the property is not set.
*/
public final class FolderConfiguration implements Comparable<FolderConfiguration> {
- public final static String QUALIFIER_SEP = "-"; //$NON-NLS-1$
+
+ private final static ResourceQualifier[] DEFAULT_QUALIFIERS;
+
+ static {
+ // get the default qualifiers.
+ FolderConfiguration defaultConfig = new FolderConfiguration();
+ defaultConfig.createDefault();
+ DEFAULT_QUALIFIERS = defaultConfig.getQualifiers();
+ }
+
private final ResourceQualifier[] mQualifiers = new ResourceQualifier[INDEX_COUNT];
@@ -48,6 +61,45 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
private final static int INDEX_COUNT = 17;
/**
+ * Creates a {@link FolderConfiguration} matching the folder segments.
+ * @param folderSegments The segments of the folder name. The first segments should contain
+ * the name of the folder
+ * @return a FolderConfiguration object, or null if the folder name isn't valid..
+ */
+ public static FolderConfiguration getConfig(String[] folderSegments) {
+ FolderConfiguration config = new FolderConfiguration();
+
+ // we are going to loop through the segments, and match them with the first
+ // available qualifier. If the segment doesn't match we try with the next qualifier.
+ // Because the order of the qualifier is fixed, we do not reset the first qualifier
+ // after each successful segment.
+ // If we run out of qualifier before processing all the segments, we fail.
+
+ int qualifierIndex = 0;
+ int qualifierCount = DEFAULT_QUALIFIERS.length;
+
+ for (int i = 1 ; i < folderSegments.length; i++) {
+ String seg = folderSegments[i];
+ if (seg.length() > 0) {
+ while (qualifierIndex < qualifierCount &&
+ DEFAULT_QUALIFIERS[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;
+ }
+ }
+
+ return config;
+ }
+
+ /**
* Returns the number of {@link ResourceQualifier} that make up a Folder configuration.
*/
public static int getQualifierCount() {
@@ -404,7 +456,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
if (qualifier != null) {
String segment = qualifier.getFolderSegment();
if (segment != null && segment.length() > 0) {
- result.append(QUALIFIER_SEP);
+ result.append(AndroidConstants.RES_QUALIFIER_SEP);
result.append(segment);
}
}
@@ -519,6 +571,114 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
}
/**
+ * Returns the best matching {@link Configurable} for this configuration.
+ *
+ * @param configurables the list of {@link Configurable} to choose from.
+ *
+ * @return an item from the given list of {@link Configurable} or null.
+ *
+ * @see http://d.android.com/guide/topics/resources/resources-i18n.html#best-match
+ */
+ public Configurable findMatchingConfigurable(List<? extends Configurable> configurables) {
+ //
+ // 1: eliminate resources that contradict the reference configuration
+ // 2: pick next qualifier type
+ // 3: check if any resources use this qualifier, if no, back to 2, else move on to 4.
+ // 4: eliminate resources that don't use this qualifier.
+ // 5: if more than one resource left, go back to 2.
+ //
+ // The precedence of the qualifiers is more important than the number of qualifiers that
+ // exactly match the device.
+
+ // 1: eliminate resources that contradict
+ ArrayList<Configurable> matchingConfigurables = new ArrayList<Configurable>();
+ for (int i = 0 ; i < configurables.size(); i++) {
+ Configurable res = configurables.get(i);
+
+ if (res.getConfiguration().isMatchFor(this)) {
+ matchingConfigurables.add(res);
+ }
+ }
+
+ // if there is only one match, just take it
+ if (matchingConfigurables.size() == 1) {
+ return matchingConfigurables.get(0);
+ } else if (matchingConfigurables.size() == 0) {
+ return null;
+ }
+
+ // 2. Loop on the qualifiers, and eliminate matches
+ final int count = FolderConfiguration.getQualifierCount();
+ for (int q = 0 ; q < count ; q++) {
+ // look to see if one configurable has this qualifier.
+ // At the same time also record the best match value for the qualifier (if applicable).
+
+ // The reference value, to find the best match.
+ // Note that this qualifier could be null. In which case any qualifier found in the
+ // possible match, will all be considered best match.
+ ResourceQualifier referenceQualifier = getQualifier(q);
+
+ boolean found = false;
+ ResourceQualifier bestMatch = null; // this is to store the best match.
+ for (Configurable configurable : matchingConfigurables) {
+ ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q);
+ if (qualifier != null) {
+ // set the flag.
+ found = true;
+
+ // Now check for a best match. If the reference qualifier is null ,
+ // any qualifier is a "best" match (we don't need to record all of them.
+ // Instead the non compatible ones are removed below)
+ if (referenceQualifier != null) {
+ if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) {
+ bestMatch = qualifier;
+ }
+ }
+ }
+ }
+
+ // 4. If a configurable has a qualifier at the current index, remove all the ones that
+ // do not have one, or whose qualifier value does not equal the best match found above
+ // unless there's no reference qualifier, in which case they are all considered
+ // "best" match.
+ if (found) {
+ for (int i = 0 ; i < matchingConfigurables.size(); ) {
+ Configurable configurable = matchingConfigurables.get(i);
+ ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q);
+
+ if (qualifier == null) {
+ // this resources has no qualifier of this type: rejected.
+ matchingConfigurables.remove(configurable);
+ } else if (referenceQualifier != null && bestMatch != null &&
+ bestMatch.equals(qualifier) == false) {
+ // there's a reference qualifier and there is a better match for it than
+ // this resource, so we reject it.
+ matchingConfigurables.remove(configurable);
+ } else {
+ // looks like we keep this resource, move on to the next one.
+ i++;
+ }
+ }
+
+ // at this point we may have run out of matching resources before going
+ // through all the qualifiers.
+ if (matchingConfigurables.size() < 2) {
+ break;
+ }
+ }
+ }
+
+ // Because we accept resources whose configuration have qualifiers where the reference
+ // configuration doesn't, we can end up with more than one match. In this case, we just
+ // take the first one.
+ if (matchingConfigurables.size() == 0) {
+ return null;
+ }
+ return matchingConfigurables.get(0);
+ }
+
+
+ /**
* Returns whether the configuration is a match for the given reference config.
* <p/>A match means that, for each qualifier of this config
* <ul>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/KeyboardStateQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/KeyboardStateQualifier.java
index 3cf6091..1ca5dad 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/KeyboardStateQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/KeyboardStateQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.KeyboardState;
import com.android.resources.ResourceEnum;
-import org.eclipse.swt.graphics.Image;
-
/**
* Resource Qualifier for keyboard state.
*/
@@ -59,11 +56,6 @@ public final class KeyboardStateQualifier extends EnumBasedResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("keyboard"); //$NON-NLS-1$
- }
-
- @Override
public boolean checkAndSet(String value, FolderConfiguration config) {
KeyboardState orientation = KeyboardState.getEnum(value);
if (orientation != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/LanguageQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/LanguageQualifier.java
index 2686eac..ff18bdc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/LanguageQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/LanguageQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-
-import org.eclipse.swt.graphics.Image;
+package com.android.ide.common.resources.configuration;
import java.util.regex.Pattern;
@@ -90,11 +86,6 @@ public final class LanguageQualifier extends ResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("language"); //$NON-NLS-1$
- }
-
- @Override
public boolean isValid() {
return mValue != null;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationMethodQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/NavigationMethodQualifier.java
index 5faa293..6c7e31f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationMethodQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/NavigationMethodQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.Navigation;
import com.android.resources.ResourceEnum;
-import org.eclipse.swt.graphics.Image;
-
/**
* Resource Qualifier for Navigation Method.
*/
@@ -58,12 +55,6 @@ public final class NavigationMethodQualifier extends EnumBasedResourceQualifier
return NAME;
}
-
- @Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("navpad"); //$NON-NLS-1$
- }
-
@Override
public boolean checkAndSet(String value, FolderConfiguration config) {
Navigation method = Navigation.getEnum(value);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationStateQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/NavigationStateQualifier.java
index 8cea2d3..9b1e07e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NavigationStateQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/NavigationStateQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.NavigationState;
import com.android.resources.ResourceEnum;
-import org.eclipse.swt.graphics.Image;
-
/**
* Resource Qualifier for navigation state.
*/
@@ -59,11 +56,6 @@ public final class NavigationStateQualifier extends EnumBasedResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("navpad"); //$NON-NLS-1$
- }
-
- @Override
public boolean checkAndSet(String value, FolderConfiguration config) {
NavigationState state = NavigationState.getEnum(value);
if (state != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NetworkCodeQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/NetworkCodeQualifier.java
index 4ef0c75..295e8ab 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NetworkCodeQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/NetworkCodeQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-
-import org.eclipse.swt.graphics.Image;
+package com.android.ide.common.resources.configuration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -98,11 +94,6 @@ public final class NetworkCodeQualifier extends ResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("mnc"); //$NON-NLS-1$
- }
-
- @Override
public boolean isValid() {
return mCode != DEFAULT_CODE;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NightModeQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/NightModeQualifier.java
index 15aea6b..9e49091 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/NightModeQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/NightModeQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.NightMode;
import com.android.resources.ResourceEnum;
-import org.eclipse.swt.graphics.Image;
-
/**
* Resource Qualifier for Navigation Method.
*/
@@ -59,11 +56,6 @@ public final class NightModeQualifier extends EnumBasedResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("nightmode"); //$NON-NLS-1$
- }
-
- @Override
public boolean checkAndSet(String value, FolderConfiguration config) {
NightMode mode = NightMode.getEnum(value);
if (mode != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/PixelDensityQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/PixelDensityQualifier.java
index 32fc0c5..80842a8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/PixelDensityQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/PixelDensityQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.Density;
import com.android.resources.ResourceEnum;
-import org.eclipse.swt.graphics.Image;
-
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -63,11 +60,6 @@ public final class PixelDensityQualifier extends EnumBasedResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("dpi"); //$NON-NLS-1$
- }
-
- @Override
public boolean checkAndSet(String value, FolderConfiguration config) {
Density density = Density.getEnum(value);
if (density == null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/RegionQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/RegionQualifier.java
index dfe02cf..7e8ca9a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/RegionQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/RegionQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-
-import org.eclipse.swt.graphics.Image;
+package com.android.ide.common.resources.configuration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -94,11 +90,6 @@ public final class RegionQualifier extends ResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("region"); //$NON-NLS-1$
- }
-
- @Override
public boolean isValid() {
return mValue != null;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ResourceQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/ResourceQualifier.java
index b4d9a34..6abac4e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ResourceQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/ResourceQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import org.eclipse.swt.graphics.Image;
/**
* Base class for resource qualifiers.
@@ -36,11 +35,6 @@ public abstract class ResourceQualifier implements Comparable<ResourceQualifier>
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();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenDimensionQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifier.java
index c9ff7c2..a58789a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenDimensionQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-
-import org.eclipse.swt.graphics.Image;
+package com.android.ide.common.resources.configuration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -75,11 +71,6 @@ public final class ScreenDimensionQualifier extends ResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("dimension"); //$NON-NLS-1$
- }
-
- @Override
public boolean isValid() {
return mValue1 != DEFAULT_SIZE && mValue2 != DEFAULT_SIZE;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenOrientationQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifier.java
index d7d6bd3..c26a6f4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenOrientationQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.ResourceEnum;
import com.android.resources.ScreenOrientation;
-import org.eclipse.swt.graphics.Image;
-
/**
* Resource Qualifier for Screen Orientation.
*/
@@ -58,11 +55,6 @@ public final class ScreenOrientationQualifier extends EnumBasedResourceQualifier
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("orientation"); //$NON-NLS-1$
- }
-
- @Override
public boolean checkAndSet(String value, FolderConfiguration config) {
ScreenOrientation orientation = ScreenOrientation.getEnum(value);
if (orientation != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenRatioQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/ScreenRatioQualifier.java
index 4444273..4cbf0a4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenRatioQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/ScreenRatioQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.ResourceEnum;
import com.android.resources.ScreenRatio;
-import org.eclipse.swt.graphics.Image;
-
public class ScreenRatioQualifier extends EnumBasedResourceQualifier {
public static final String NAME = "Screen Ratio";
@@ -55,11 +52,6 @@ public class ScreenRatioQualifier extends EnumBasedResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("ratio"); //$NON-NLS-1$
- }
-
- @Override
public boolean checkAndSet(String value, FolderConfiguration config) {
ScreenRatio size = ScreenRatio.getEnum(value);
if (size != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenSizeQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/ScreenSizeQualifier.java
index 023a861..7ab6dd8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/ScreenSizeQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/ScreenSizeQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.ResourceEnum;
import com.android.resources.ScreenSize;
-import org.eclipse.swt.graphics.Image;
-
/**
* Resource Qualifier for Screen Size. Size can be "small", "normal", "large" and "x-large"
*/
@@ -59,11 +56,6 @@ public class ScreenSizeQualifier extends EnumBasedResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("size"); //$NON-NLS-1$
- }
-
- @Override
public boolean checkAndSet(String value, FolderConfiguration config) {
ScreenSize size = ScreenSize.getEnum(value);
if (size != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/TextInputMethodQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/TextInputMethodQualifier.java
index b5ce166..3d772aa 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/TextInputMethodQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/TextInputMethodQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.Keyboard;
import com.android.resources.ResourceEnum;
-import org.eclipse.swt.graphics.Image;
-
/**
* Resource Qualifier for Text Input Method.
*/
@@ -60,11 +57,6 @@ public final class TextInputMethodQualifier extends EnumBasedResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("text_input"); //$NON-NLS-1$
- }
-
- @Override
public boolean checkAndSet(String value, FolderConfiguration config) {
Keyboard method = Keyboard.getEnum(value);
if (method != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/TouchScreenQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/TouchScreenQualifier.java
index f3b8eb0..eeb68d2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/TouchScreenQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/TouchScreenQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.resources.ResourceEnum;
import com.android.resources.TouchScreen;
-import org.eclipse.swt.graphics.Image;
-
/**
* Resource Qualifier for Touch Screen type.
@@ -60,11 +57,6 @@ public final class TouchScreenQualifier extends EnumBasedResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("touch"); //$NON-NLS-1$
- }
-
- @Override
public boolean checkAndSet(String value, FolderConfiguration config) {
TouchScreen type = TouchScreen.getEnum(value);
if (type != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/VersionQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java
index 199e804..c7cef97 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/VersionQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-
-import org.eclipse.swt.graphics.Image;
+package com.android.ide.common.resources.configuration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -99,11 +95,6 @@ public final class VersionQualifier extends ResourceQualifier {
}
@Override
- public Image getIcon() {
- return IconFactory.getInstance().getIcon("version"); //$NON-NLS-1$
- }
-
- @Override
public boolean isValid() {
return mVersion != DEFAULT_VERSION;
}
diff --git a/ide_common/tests/.classpath b/ide_common/tests/.classpath
new file mode 100644
index 0000000..3cc56e3
--- /dev/null
+++ b/ide_common/tests/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/ide_common"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/common"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/ide_common/tests/.project b/ide_common/tests/.project
new file mode 100644
index 0000000..1cfa60e
--- /dev/null
+++ b/ide_common/tests/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>ide_common-tests</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/draw9patch/src/Android.mk b/ide_common/tests/Android.mk
index 3dc9db4..9443dba 100644
--- a/draw9patch/src/Android.mk
+++ b/ide_common/tests/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2008 The Android Open Source Project
+# Copyright (C) 2011 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.
@@ -13,14 +13,15 @@
# limitations under the License.
LOCAL_PATH := $(call my-dir)
+
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_JAVA_RESOURCE_DIRS := resources
+# Only compile source java files in this lib.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := ide_common-tests
+LOCAL_MODULE_TAGS := optional
-LOCAL_JAR_MANIFEST := ../etc/manifest.txt
-LOCAL_JAVA_LIBRARIES := \
- swing-worker-1.1
-LOCAL_MODULE := draw9patch
+LOCAL_JAVA_LIBRARIES := common ide_common junit
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/CountryCodeQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/CountryCodeQualifierTest.java
index e0fe013..eba8b8d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/CountryCodeQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/CountryCodeQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.resources.configurations.CountryCodeQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+package com.android.ide.common.resources.configuration;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/DockModeQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/DockModeQualifierTest.java
index de05f7e..195d474 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/DockModeQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/DockModeQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.DockModeQualifier;
import com.android.resources.DockMode;
import junit.framework.TestCase;
diff --git a/ide_common/tests/src/com/android/ide/common/resources/configuration/FolderConfigurationTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/FolderConfigurationTest.java
new file mode 100644
index 0000000..6d0d487
--- /dev/null
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/FolderConfigurationTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 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.ide.common.resources.configuration;
+
+import junit.framework.TestCase;
+
+public class FolderConfigurationTest extends TestCase {
+
+ /*
+ * Test createDefault creates all the qualifiers.
+ */
+ public void testCreateDefault() {
+ FolderConfiguration defaultConfig = new FolderConfiguration();
+ defaultConfig.createDefault();
+
+ // this is always valid and up to date.
+ final int count = FolderConfiguration.getQualifierCount();
+
+ // make sure all the qualifiers were created.
+ for (int i = 0 ; i < count ; i++) {
+ assertNotNull(defaultConfig.getQualifier(i));
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/KeyboardStateQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/KeyboardStateQualifierTest.java
index b97d1f8..cf52a38 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/KeyboardStateQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/KeyboardStateQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier;
import com.android.resources.KeyboardState;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/LanguageQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/LanguageQualifierTest.java
index e4a9312..2dfd65f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/LanguageQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/LanguageQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,15 +14,12 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
+package com.android.ide.common.resources.configuration;
import junit.framework.TestCase;
public class LanguageQualifierTest extends TestCase {
-
+
private FolderConfiguration config;
private LanguageQualifier lq;
@@ -39,14 +36,14 @@ public class LanguageQualifierTest extends TestCase {
config = null;
lq = null;
}
-
+
public void testCheckAndSet() {
assertEquals(true, lq.checkAndSet("en", config)); //$NON-NLS-1$
assertTrue(config.getLanguageQualifier() != null);
assertEquals("en", config.getLanguageQualifier().toString()); //$NON-NLS-1$
-
+
}
-
+
public void testFailures() {
assertEquals(false, lq.checkAndSet("", config)); //$NON-NLS-1$
assertEquals(false, lq.checkAndSet("EN", config)); //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/NavigationMethodQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/NavigationMethodQualifierTest.java
index ebc34ad..4237dde 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/NavigationMethodQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/NavigationMethodQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier;
import com.android.resources.Navigation;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/NetworkCodeQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/NetworkCodeQualifierTest.java
index 7512305..6896316 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/NetworkCodeQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/NetworkCodeQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.NetworkCodeQualifier;
+package com.android.ide.common.resources.configuration;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/PixelDensityQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/PixelDensityQualifierTest.java
index 747e6d7..b99f2af 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/PixelDensityQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/PixelDensityQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
import com.android.resources.Density;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/RegionQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/RegionQualifierTest.java
index a70a04b..fc0402c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/RegionQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/RegionQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
+package com.android.ide.common.resources.configuration;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/ScreenDimensionQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifierTest.java
index b25fb76..e57424f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/ScreenDimensionQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
-
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier;
+package com.android.ide.common.resources.configuration;
import junit.framework.TestCase;
@@ -39,7 +36,7 @@ public class ScreenDimensionQualifierTest extends TestCase {
sdq = null;
config = null;
}
-
+
public void testCheckAndSet() {
assertEquals(true, sdq.checkAndSet("400x200", config));//$NON-NLS-1$
assertTrue(config.getScreenDimensionQualifier() != null);
@@ -47,7 +44,7 @@ public class ScreenDimensionQualifierTest extends TestCase {
assertEquals(200, config.getScreenDimensionQualifier().getValue2());
assertEquals("400x200", config.getScreenDimensionQualifier().toString()); //$NON-NLS-1$
}
-
+
public void testFailures() {
assertEquals(false, sdq.checkAndSet("", config));//$NON-NLS-1$
assertEquals(false, sdq.checkAndSet("400X200", config));//$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/ScreenOrientationQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java
index b960c97..3aac5f3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/ScreenOrientationQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier;
import com.android.resources.ScreenOrientation;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/ScreenSizeQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/ScreenSizeQualifierTest.java
index 26cf1f3..b19f125 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/ScreenSizeQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/ScreenSizeQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier;
import com.android.resources.ScreenSize;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/TextInputMethodQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/TextInputMethodQualifierTest.java
index f9ba80b..bc2c890 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/TextInputMethodQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/TextInputMethodQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier;
import com.android.resources.Keyboard;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/TouchScreenQualifierTest.java b/ide_common/tests/src/com/android/ide/common/resources/configuration/TouchScreenQualifierTest.java
index 1dafa8b..04f8a30 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/configurations/TouchScreenQualifierTest.java
+++ b/ide_common/tests/src/com/android/ide/common/resources/configuration/TouchScreenQualifierTest.java
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ * 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,
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.resources.configurations;
+package com.android.ide.common.resources.configuration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier;
import com.android.resources.TouchScreen;
import junit.framework.TestCase;
diff --git a/layoutlib_api/.settings/org.eclipse.jdt.core.prefs b/layoutlib_api/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1cb4685
--- /dev/null
+++ b/layoutlib_api/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 09 14:02:32 PST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/layoutlib_api/NOTICE b/layoutlib_api/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/layoutlib_api/NOTICE
@@ -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/layoutlib_api/src/com/android/ide/common/rendering/api/AdapterBinding.java b/layoutlib_api/src/com/android/ide/common/rendering/api/AdapterBinding.java
new file mode 100644
index 0000000..9481246
--- /dev/null
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/AdapterBinding.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 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.ide.common.rendering.api;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Describe the content of the dynamic android.widget.Adapter used to fill
+ * android.widget.AdapterView
+ */
+public class AdapterBinding implements Iterable<DataBindingItem> {
+
+ private final int mRepeatCount;
+ private final List<ResourceReference> mHeaders = new ArrayList<ResourceReference>();
+ private final List<DataBindingItem> mItems = new ArrayList<DataBindingItem>();
+ private final List<ResourceReference> mFooters = new ArrayList<ResourceReference>();
+
+ public AdapterBinding(int repeatCount) {
+ mRepeatCount = repeatCount;
+ }
+
+ public int getRepeatCount() {
+ return mRepeatCount;
+ }
+
+ public void addHeader(ResourceReference layoutInfo) {
+ mHeaders.add(layoutInfo);
+ }
+
+ public int getHeaderCount() {
+ return mHeaders.size();
+ }
+
+ public ResourceReference getHeaderAt(int index) {
+ return mHeaders.get(index);
+ }
+
+ public void addItem(DataBindingItem item) {
+ mItems.add(item);
+ }
+
+ public int getItemCount() {
+ return mItems.size();
+ }
+
+ public DataBindingItem getItemAt(int index) {
+ return mItems.get(index);
+ }
+
+ public void addFooter(ResourceReference layoutInfo) {
+ mFooters.add(layoutInfo);
+ }
+
+ public int getFooterCount() {
+ return mFooters.size();
+ }
+
+ public ResourceReference getFooterAt(int index) {
+ return mFooters.get(index);
+ }
+
+ public Iterator<DataBindingItem> iterator() {
+ return mItems.iterator();
+ }
+}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/Bridge.java b/layoutlib_api/src/com/android/ide/common/rendering/api/Bridge.java
index 48309cf..c044353 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/Bridge.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/Bridge.java
@@ -17,6 +17,8 @@
package com.android.ide.common.rendering.api;
+import static com.android.ide.common.rendering.api.Result.Status.NOT_IMPLEMENTED;
+
import com.android.ide.common.rendering.api.Result.Status;
import java.awt.image.BufferedImage;
@@ -109,4 +111,40 @@ public abstract class Bridge {
public void clearCaches(Object projectKey) {
}
+
+ /**
+ * Utility method returning the parent of a given view object.
+ *
+ * @param viewObject the object for which to return the parent.
+ *
+ * @return a {@link Result} indicating the status of the action, and if success, the parent
+ * object in {@link Result#getData()}
+ */
+ public Result getViewParent(Object viewObject) {
+ return NOT_IMPLEMENTED.createResult();
+ }
+
+ /**
+ * Utility method returning the index of a given view in its parent.
+ * @param viewObject the object for which to return the index.
+ *
+ * @return a {@link Result} indicating the status of the action, and if success, the index in
+ * the parent in {@link Result#getData()}
+ */
+ public Result getViewIndex(Object viewObject) {
+ return NOT_IMPLEMENTED.createResult();
+ }
+
+ /**
+ * Utility method returning the baseline value for a given view object. This basically returns
+ * View.getBaseline().
+ *
+ * @param viewObject the object for which to return the index.
+ *
+ * @return the baseline value or -1 if not applicable to the view object or if this layout
+ * library does not implement this method.
+ */
+ public int getViewBaseline(Object viewObject) {
+ return -1;
+ }
}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java b/layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java
index ff6777b..6620571 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/Capability.java
@@ -24,38 +24,41 @@ public enum Capability {
/** Ability to render at full size, as required by the layout, and unbound by the screen */
UNBOUND_RENDERING,
/** Ability to override the background of the rendering with transparency using
- * {@link SceneParams#setCustomBackgroundColor(int)} */
+ * {@link SessionParams#setOverrideBgColor(int)} */
CUSTOM_BACKGROUND_COLOR,
- /** Ability to call {@link LayoutScene#render()} and {@link LayoutScene#render(long)}. */
+ /** Ability to call {@link RenderSession#render()} and {@link RenderSession#render(long)}. */
RENDER,
+ /** Ability to ask for a layout only with no rendering through
+ * {@link SessionParams#setLayoutOnly()}
+ */
+ LAYOUT_ONLY,
/**
- * Ability to control embedded layout parsers through {@link IXmlPullParser#getParser(String)}
+ * Ability to control embedded layout parsers through {@link ILayoutPullParser#getParser(String)}
*/
EMBEDDED_LAYOUT,
/** Ability to call<br>
- * {@link LayoutScene#insertChild(Object, IXmlPullParser, int, com.android.layoutlib.api.LayoutScene.IAnimationListener)}<br>
- * {@link LayoutScene#moveChild(Object, Object, int, java.util.Map, com.android.layoutlib.api.LayoutScene.IAnimationListener)}<br>
- * {@link LayoutScene#removeChild(Object, com.android.layoutlib.api.LayoutScene.IAnimationListener)}<br>
- * {@link LayoutScene#setProperty(Object, String, String)}<br>
+ * {@link RenderSession#insertChild(Object, ILayoutPullParser, int, IAnimationListener)}<br>
+ * {@link RenderSession#moveChild(Object, Object, int, java.util.Map, IAnimationListener)}<br>
+ * {@link RenderSession#setProperty(Object, String, String)}<br>
* The method that receives an animation listener can only use it if the
* ANIMATED_VIEW_MANIPULATION, or FULL_ANIMATED_VIEW_MANIPULATION is also supported.
- *
* */
VIEW_MANIPULATION,
/** Ability to play animations with<br>
- * {@link LayoutScene#animate(Object, String, boolean, com.android.layoutlib.api.LayoutScene.IAnimationListener)}
+ * {@link RenderSession#animate(Object, String, boolean, IAnimationListener)}
*/
PLAY_ANIMATION,
/**
* Ability to manipulate views with animation, as long as the view does not change parent.
- * {@link LayoutScene#insertChild(Object, IXmlPullParser, int, com.android.layoutlib.api.LayoutScene.IAnimationListener)}<br>
- * {@link LayoutScene#moveChild(Object, Object, int, java.util.Map, com.android.layoutlib.api.LayoutScene.IAnimationListener)}<br>
- * {@link LayoutScene#removeChild(Object, com.android.layoutlib.api.LayoutScene.IAnimationListener)}<br>
+ * {@link RenderSession#insertChild(Object, ILayoutPullParser, int, IAnimationListener)}<br>
+ * {@link RenderSession#moveChild(Object, Object, int, java.util.Map, IAnimationListener)}<br>
+ * {@link RenderSession#removeChild(Object, IAnimationListener)}<br>
*/
ANIMATED_VIEW_MANIPULATION,
/**
* Ability to move views (even into a different ViewGroup) with animation.
- * see {@link LayoutScene#moveChild(Object, Object, int, java.util.Map, com.android.layoutlib.api.LayoutScene.IAnimationListener)}
+ * see {@link RenderSession#moveChild(Object, Object, int, java.util.Map, IAnimationListener)}
*/
- FULL_ANIMATED_VIEW_MANIPULATION;
+ FULL_ANIMATED_VIEW_MANIPULATION,
+ ADAPTER_BINDING;
}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/DataBindingItem.java b/layoutlib_api/src/com/android/ide/common/rendering/api/DataBindingItem.java
new file mode 100644
index 0000000..93569bd
--- /dev/null
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/DataBindingItem.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 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.ide.common.rendering.api;
+
+import com.android.resources.ResourceType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A data binding item. It contain a {@link ResourceReference} to the view used to represent it.
+ * It also contains how many items of this type the AdapterView should display.
+ *
+ * It can also contain an optional list of children in case the AdapterView is an
+ * ExpandableListView. In this case, the count value is used as a repeat count for the children,
+ * similar to {@link AdapterBinding#getRepeatCount()}.
+ *
+ */
+public class DataBindingItem implements Iterable<DataBindingItem> {
+ private final ResourceReference mReference;
+ private final int mCount;
+ private List<DataBindingItem> mChildren;
+
+ public DataBindingItem(ResourceReference reference, int count) {
+ mReference = reference;
+ mCount = count;
+ }
+
+ public DataBindingItem(String name, boolean platformLayout, int count) {
+ this(new ResourceReference(name, platformLayout), count);
+ }
+
+ public DataBindingItem(String name, boolean platformLayout) {
+ this(name, platformLayout, 1);
+ }
+
+ public DataBindingItem(String name, int count) {
+ this(name, false /*platformLayout*/, count);
+ }
+
+ public DataBindingItem(String name) {
+ this(name, false /*platformLayout*/, 1);
+ }
+
+ /**
+ * Returns the {@link ResourceReference} for the view. The {@link ResourceType} for the
+ * referenced resource is implied to be {@link ResourceType#LAYOUT}.
+ */
+ public ResourceReference getViewReference() {
+ return mReference;
+ }
+
+ /**
+ * The repeat count for this object or the repeat count for the children if there are any.
+ */
+ public int getCount() {
+ return mCount;
+ }
+
+ public void addChild(DataBindingItem child) {
+ if (mChildren == null) {
+ mChildren = new ArrayList<DataBindingItem>();
+ }
+
+ mChildren.add(child);
+ }
+
+ public List<DataBindingItem> getChildren() {
+ if (mChildren != null) {
+ return mChildren;
+ }
+
+ return Collections.emptyList();
+ }
+
+ public Iterator<DataBindingItem> iterator() {
+ List<DataBindingItem> list = getChildren();
+ return list.iterator();
+ }
+}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/DensityBasedResourceValue.java b/layoutlib_api/src/com/android/ide/common/rendering/api/DensityBasedResourceValue.java
index ca60640..f63f16f 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/DensityBasedResourceValue.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/DensityBasedResourceValue.java
@@ -41,6 +41,7 @@ public class DensityBasedResourceValue extends ResourceValue implements IDensity
/** Legacy method, do not call
* @deprecated use {@link #getResourceDensity()} instead.
*/
+ @Deprecated
public Density getDensity() {
return Density.getEnum(mDensity.getDpiValue());
}
@@ -51,4 +52,35 @@ public class DensityBasedResourceValue extends ResourceValue implements IDensity
+ getResourceType() + "/" + getName() + " = " + getValue()
+ " (density:" + mDensity +", framework:" + isFramework() + ")]";
}
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((mDensity == null) ? 0 : mDensity.hashCode());
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ DensityBasedResourceValue other = (DensityBasedResourceValue) obj;
+ if (mDensity == null) {
+ if (other.mDensity != null)
+ return false;
+ } else if (!mDensity.equals(other.mDensity))
+ return false;
+ return true;
+ }
}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/ILayoutPullParser.java b/layoutlib_api/src/com/android/ide/common/rendering/api/ILayoutPullParser.java
index 4b033d9..574f9bb 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/ILayoutPullParser.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/ILayoutPullParser.java
@@ -38,8 +38,9 @@ public interface ILayoutPullParser extends XmlPullParser {
* @param layoutName the name of the layout.
* @return returns a custom parser or null if no custom parsers are needed.
*
- * @since 5
+ * @deprecated use {@link IProjectCallback#getParser(String)} instead
*/
+ @Deprecated
ILayoutPullParser getParser(String layoutName);
}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/IProjectCallback.java b/layoutlib_api/src/com/android/ide/common/rendering/api/IProjectCallback.java
index 0ec214f..b91b598 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/IProjectCallback.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/IProjectCallback.java
@@ -19,6 +19,8 @@ package com.android.ide.common.rendering.api;
import com.android.resources.ResourceType;
import com.android.util.Pair;
+import java.net.URL;
+
/**
* Callback for project information needed by the Layout Library.
* Classes implementing this interface provide methods giving access to some project data, like
@@ -26,6 +28,23 @@ import com.android.util.Pair;
*/
public interface IProjectCallback {
+ public enum ViewAttribute {
+ TEXT(String.class),
+ IS_CHECKED(Boolean.class),
+ SRC(URL.class),
+ COLOR(Integer.class);
+
+ private final Class<?> mClass;
+
+ private ViewAttribute(Class<?> theClass) {
+ mClass = theClass;
+ }
+
+ public Class<?> getAttributeClass() {
+ return mClass;
+ }
+ }
+
/**
* Loads a custom view with the given constructor signature and arguments.
* @param name The fully qualified name of the class.
@@ -73,4 +92,52 @@ public interface IProjectCallback {
* @return an Integer containing the resource Id, or <code>null</code> if not found.
*/
Integer getResourceId(ResourceType type, String name);
+
+ /**
+ * Returns a custom parser for the layout of the given name.
+ * @param layoutName the name of the layout.
+ * @return returns a custom parser or null if no custom parsers are needed.
+ */
+ ILayoutPullParser getParser(String layoutName);
+
+ /**
+ * Returns the value of an item used by an adapter.
+ * @param adapterView The {@link ResourceReference} for the adapter view info.
+ * @param adapterCookie the view cookie for this particular view.
+ * @param itemRef the {@link ResourceReference} for the layout used by the adapter item.
+ * @param fullPosition the position of the item in the full list.
+ * @param positionPerType the position of the item if only items of the same type are
+ * considered. If there is only one type of items, this is the same as
+ * <var>fullPosition</var>.
+ * @param fullParentPosition the full position of the item's parent. This is only
+ * valid if the adapter view is an ExpandableListView.
+ * @param parentPositionPerType the position of the parent's item, only considering items
+ * of the same type. This is only valid if the adapter view is an ExpandableListView.
+ * If there is only one type of items, this is the same as <var>fullParentPosition</var>.
+ * @param viewRef The {@link ResourceReference} for the view we're trying to fill.
+ * @param ViewAttribute the attribute being queried.
+ * @param defaultValue the default value for this attribute. The object class matches the
+ * class associated with the {@link ViewAttribute}.
+ * @return the item value or null if there's no value.
+ *
+ * @see ViewAttribute#getAttributeClass()
+ */
+ Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
+ ResourceReference itemRef,
+ int fullPosition, int positionPerType,
+ int fullParentPosition, int parentPositionPerType,
+ ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue);
+
+ /**
+ * Returns an adapter binding for a given adapter view.
+ * This is only called if {@link SessionParams} does not have an {@link AdapterBinding} for
+ * the given {@link ResourceReference} already.
+ *
+ * @param adapterViewRef the reference of adapter view to return the adapter binding for.
+ * @param adapterCookie the view cookie for this particular view.
+ * @param viewObject the view object for the adapter.
+ * @return an adapter binding for the given view or null if there's no data.
+ */
+ AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
+ Object viewObject);
}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/RenderSession.java b/layoutlib_api/src/com/android/ide/common/rendering/api/RenderSession.java
index a2e087c..188909e 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/RenderSession.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/RenderSession.java
@@ -162,29 +162,6 @@ public class RenderSession {
}
/**
- * Returns the View parent.
- *
- * @param viewObject the object for which to return the parent.
- *
- * @return a {@link Result} indicating the status of the action, and if success, the parent
- * object in {@link Result#getData()}
- */
- public Result getViewParent(Object viewObject) {
- return NOT_IMPLEMENTED.createResult();
- }
-
- /**
- * Returns the index of a given view it its parent.
- * @param viewObject the object for which to return the index.
- *
- * @return a {@link Result} indicating the status of the action, and if success, the index in
- * the parent in {@link Result#getData()}
- */
- public Result getViewIndex(Object viewObject) {
- return NOT_IMPLEMENTED.createResult();
- }
-
- /**
* Inserts a new child in a ViewGroup object, and renders the result.
* <p/>
* The child is first inflated and then added to its new parent, at the given <var>index<var>
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/ResourceReference.java b/layoutlib_api/src/com/android/ide/common/rendering/api/ResourceReference.java
new file mode 100644
index 0000000..f22f51e
--- /dev/null
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/ResourceReference.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 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.ide.common.rendering.api;
+
+/**
+ * A resource reference. This contains the String ID of the resource and whether this is a framework
+ * reference.
+ * This is an immutable class.
+ *
+ */
+public class ResourceReference {
+ private final String mName;
+ private final boolean mIsFramework;
+
+ /**
+ * Builds a resource reference.
+ * @param name the name of the resource
+ * @param isFramework whether the reference is to a framework resource.
+ */
+ public ResourceReference(String name, boolean isFramework) {
+ mName = name;
+ mIsFramework = isFramework;
+ }
+
+ /**
+ * Builds a non-framework resource reference.
+ * @param name the name of the resource
+ */
+ public ResourceReference(String name) {
+ this(name, false /*platformLayout*/);
+ }
+
+ /**
+ * Returns the name of the resource, as defined in the XML.
+ */
+ public final String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns whether the resource is a framework resource (<code>true</code>) or a project
+ * resource (<code>false</false>).
+ */
+ public final boolean isFramework() {
+ return mIsFramework;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (mIsFramework ? 1231 : 1237);
+ result = prime * result + ((mName == null) ? 0 : mName.hashCode());
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ResourceReference other = (ResourceReference) obj;
+ if (mIsFramework != other.mIsFramework)
+ return false;
+ if (mName == null) {
+ if (other.mName != null)
+ return false;
+ } else if (!mName.equals(other.mName))
+ return false;
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "ResourceReference [" + mName + " (framework:" + mIsFramework+ ")]";
+ }
+}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/ResourceValue.java b/layoutlib_api/src/com/android/ide/common/rendering/api/ResourceValue.java
index f15d903..bb7dab4 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/ResourceValue.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/ResourceValue.java
@@ -23,23 +23,19 @@ import com.android.resources.ResourceType;
* Represents an android resource with a name and a string value.
*/
@SuppressWarnings("deprecation")
-public class ResourceValue implements IResourceValue {
+public class ResourceValue extends ResourceReference implements IResourceValue {
private final ResourceType mType;
- private final String mName;
private String mValue = null;
- private final boolean mIsFramwork;
- public ResourceValue(ResourceType type, String name, boolean isFramwork) {
+ public ResourceValue(ResourceType type, String name, boolean isFramework) {
+ super(name, isFramework);
mType = type;
- mName = name;
- mIsFramwork = isFramwork;
}
public ResourceValue(ResourceType type, String name, String value, boolean isFramework) {
+ super(name, isFramework);
mType = type;
- mName = name;
mValue = value;
- mIsFramwork = isFramework;
}
public ResourceType getResourceType() {
@@ -50,18 +46,12 @@ public class ResourceValue implements IResourceValue {
* Returns the type of the resource. For instance "drawable", "color", etc...
* @deprecated use {@link #getResourceType()} instead.
*/
+ @Deprecated
public String getType() {
return mType.getName();
}
/**
- * Returns the name of the resource, as defined in the XML.
- */
- public final String getName() {
- return mName;
- }
-
- /**
* Returns the value of the resource, as defined in the XML. This can be <code>null</code>
*/
public final String getValue() {
@@ -69,14 +59,6 @@ public class ResourceValue implements IResourceValue {
}
/**
- * Returns whether the resource is a framework resource (<code>true</code>) or a project
- * resource (<code>false</false>).
- */
- public final boolean isFramework() {
- return mIsFramwork;
- }
-
- /**
* Sets the value of the resource.
* @param value the new value
*/
@@ -94,9 +76,44 @@ public class ResourceValue implements IResourceValue {
@Override
public String toString() {
- return "ResourceValue [" + mType + "/" + mName + " = " + mValue
- + " (framework:" + mIsFramwork + ")]";
+ return "ResourceValue [" + mType + "/" + getName() + " = " + mValue //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + " (framework:" + isFramework() + ")]"; //$NON-NLS-1$ //$NON-NLS-2$
}
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((mType == null) ? 0 : mType.hashCode());
+ result = prime * result + ((mValue == null) ? 0 : mValue.hashCode());
+ return result;
+ }
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ResourceValue other = (ResourceValue) obj;
+ if (mType == null) {
+ if (other.mType != null)
+ return false;
+ } else if (!mType.equals(other.mType))
+ return false;
+ if (mValue == null) {
+ if (other.mValue != null)
+ return false;
+ } else if (!mValue.equals(other.mValue))
+ return false;
+ return true;
+ }
}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/SessionParams.java b/layoutlib_api/src/com/android/ide/common/rendering/api/SessionParams.java
index 9446ff5..f4f6b5c 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/SessionParams.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/SessionParams.java
@@ -18,9 +18,12 @@ package com.android.ide.common.rendering.api;
import com.android.resources.Density;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Rendering parameters for a {@link RenderSession}.
- *
*/
public class SessionParams extends RenderParams {
@@ -47,69 +50,94 @@ public class SessionParams extends RenderParams {
}
}
-
private final ILayoutPullParser mLayoutDescription;
private final RenderingMode mRenderingMode;
+ private boolean mLayoutOnly = false;
+ private Map<ResourceReference, AdapterBinding> mAdapterBindingMap;
/**
- *
- * @param layoutDescription the {@link ILayoutPullParser} letting the LayoutLib Bridge visit the
- * layout file.
- * @param renderingMode The rendering mode.
- * @param projectKey An Object identifying the project. This is used for the cache mechanism.
- * @param screenWidth the screen width
- * @param screenHeight the screen height
- * @param density the density factor for the screen.
- * @param xdpi the screen actual dpi in X
- * @param ydpi the screen actual dpi in Y
- * @param themeName The name of the theme to use.
- * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme.
- * @param projectResources the resources of the project. The map contains (String, map) pairs
- * where the string is the type of the resource reference used in the layout file, and the
- * map contains (String, {@link ResourceValue}) pairs where the key is the resource name,
- * and the value is the resource value.
- * @param frameworkResources the framework resources. The map contains (String, map) pairs
- * where the string is the type of the resource reference used in the layout file, and the map
- * contains (String, {@link ResourceValue}) pairs where the key is the resource name, and the
- * value is the resource value.
- * @param projectCallback The {@link IProjectCallback} object to get information from
- * the project.
- * @param minSdkVersion the minSdkVersion of the project
- * @param targetSdkVersion the targetSdkVersion of the project
- * @param log the object responsible for displaying warning/errors to the user.
- */
- public SessionParams(
- ILayoutPullParser layoutDescription,
- RenderingMode renderingMode,
- Object projectKey,
- int screenWidth, int screenHeight,
- Density density, float xdpi, float ydpi,
- RenderResources renderResources,
- IProjectCallback projectCallback,
- int minSdkVersion, int targetSdkVersion,
- LayoutLog log) {
- super(projectKey, screenWidth, screenHeight, density, xdpi, ydpi,
- renderResources, projectCallback, minSdkVersion, targetSdkVersion, log);
-
- mLayoutDescription = layoutDescription;
- mRenderingMode = renderingMode;
-
- }
-
- public SessionParams(SessionParams params) {
- super(params);
- mLayoutDescription = params.mLayoutDescription;
- mRenderingMode = params.mRenderingMode;
- }
-
- public ILayoutPullParser getLayoutDescription() {
- return mLayoutDescription;
- }
-
- public RenderingMode getRenderingMode() {
- return mRenderingMode;
- }
+ *
+ * @param layoutDescription the {@link ILayoutPullParser} letting the LayoutLib Bridge visit the
+ * layout file.
+ * @param renderingMode The rendering mode.
+ * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+ * @param screenWidth the screen width
+ * @param screenHeight the screen height
+ * @param density the density factor for the screen.
+ * @param xdpi the screen actual dpi in X
+ * @param ydpi the screen actual dpi in Y
+ * @param themeName The name of the theme to use.
+ * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme.
+ * @param projectResources the resources of the project. The map contains (String, map) pairs
+ * where the string is the type of the resource reference used in the layout file, and the
+ * map contains (String, {@link ResourceValue}) pairs where the key is the resource name,
+ * and the value is the resource value.
+ * @param frameworkResources the framework resources. The map contains (String, map) pairs
+ * where the string is the type of the resource reference used in the layout file, and the map
+ * contains (String, {@link ResourceValue}) pairs where the key is the resource name, and the
+ * value is the resource value.
+ * @param projectCallback The {@link IProjectCallback} object to get information from
+ * the project.
+ * @param minSdkVersion the minSdkVersion of the project
+ * @param targetSdkVersion the targetSdkVersion of the project
+ * @param log the object responsible for displaying warning/errors to the user.
+ */
+ public SessionParams(
+ ILayoutPullParser layoutDescription,
+ RenderingMode renderingMode,
+ Object projectKey,
+ int screenWidth, int screenHeight,
+ Density density, float xdpi, float ydpi,
+ RenderResources renderResources,
+ IProjectCallback projectCallback,
+ int minSdkVersion, int targetSdkVersion,
+ LayoutLog log) {
+ super(projectKey, screenWidth, screenHeight, density, xdpi, ydpi,
+ renderResources, projectCallback, minSdkVersion, targetSdkVersion, log);
+
+ mLayoutDescription = layoutDescription;
+ mRenderingMode = renderingMode;
+ }
+
+ public SessionParams(SessionParams params) {
+ super(params);
+ mLayoutDescription = params.mLayoutDescription;
+ mRenderingMode = params.mRenderingMode;
+ if (params.mAdapterBindingMap != null) {
+ mAdapterBindingMap = new HashMap<ResourceReference, AdapterBinding>(
+ params.mAdapterBindingMap);
+ }
+ }
+
+ public ILayoutPullParser getLayoutDescription() {
+ return mLayoutDescription;
+ }
+
+ public RenderingMode getRenderingMode() {
+ return mRenderingMode;
+ }
+
+ public void setLayoutOnly() {
+ mLayoutOnly = true;
+ }
+
+ public boolean isLayoutOnly() {
+ return mLayoutOnly;
+ }
+ public void addAdapterBinding(ResourceReference reference, AdapterBinding data) {
+ if (mAdapterBindingMap == null) {
+ mAdapterBindingMap = new HashMap<ResourceReference, AdapterBinding>();
+ }
+ mAdapterBindingMap.put(reference, data);
+ }
+ public Map<ResourceReference, AdapterBinding> getAdapterBindings() {
+ if (mAdapterBindingMap == null) {
+ return Collections.emptyMap();
+ }
+
+ return Collections.unmodifiableMap(mAdapterBindingMap);
+ }
}
diff --git a/layoutlib_api/src/com/android/ide/common/rendering/api/StyleResourceValue.java b/layoutlib_api/src/com/android/ide/common/rendering/api/StyleResourceValue.java
index 429bd26..9d1e65d 100644
--- a/layoutlib_api/src/com/android/ide/common/rendering/api/StyleResourceValue.java
+++ b/layoutlib_api/src/com/android/ide/common/rendering/api/StyleResourceValue.java
@@ -75,8 +75,8 @@ public final class StyleResourceValue extends ResourceValue implements IStyleRes
* Legacy method.
* @deprecated use {@link #getValue()}
*/
+ @Deprecated
public IResourceValue findItem(String name) {
return mItems.get(name);
}
-
}
diff --git a/layoutopt/NOTICE b/layoutopt/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/layoutopt/NOTICE
@@ -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/monkeyrunner/etc/monkeyrunner b/monkeyrunner/etc/monkeyrunner
index 364be2a..fe9be0c 100755
--- a/monkeyrunner/etc/monkeyrunner
+++ b/monkeyrunner/etc/monkeyrunner
@@ -69,6 +69,29 @@ else
jarpath="$frameworkdir/$jarfile"
fi
+# Figure out the path to the swt.jar for the current architecture.
+# if ANDROID_SWT is defined, then just use this.
+# else, if running in the Android source tree, then look for the correct swt folder in prebuilt
+# else, look for the correct swt folder in the SDK under tools/lib/
+swtpath=""
+if [ -n "$ANDROID_SWT" ]; then
+ swtpath="$ANDROID_SWT"
+else
+ vmarch=`java -jar "${frameworkdir}"/archquery.jar`
+ if [ -n "$ANDROID_BUILD_TOP" ]; then
+ osname=`uname -s | tr A-Z a-z`
+ swtpath="${ANDROID_BUILD_TOP}/prebuilt/${osname}-${vmarch}/swt"
+ else
+ swtpath="${frameworkdir}/${vmarch}"
+ fi
+fi
+
+if [ ! -d "$swtpath" ]; then
+ echo "SWT folder '${swtpath}' does not exist."
+ echo "Please export ANDROID_SWT to point to the folder containing swt.jar for your platform."
+ exit 1
+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" "$@"
+exec java -Xmx128M $os_opts $java_debug -Djava.ext.dirs="$frameworkdir:$swtpath" -Djava.library.path="$libdir" -Dcom.android.monkeyrunner.bindir="$progdir" -jar "$jarpath" "$@"
diff --git a/monkeyrunner/etc/monkeyrunner.bat b/monkeyrunner/etc/monkeyrunner.bat
index 1cf38ca..5028b3f 100644
--- a/monkeyrunner/etc/monkeyrunner.bat
+++ b/monkeyrunner/etc/monkeyrunner.bat
@@ -43,4 +43,21 @@ if exist %frameworkdir%%jarfile% goto JarFileOk
set jarpath=%frameworkdir%%jarfile%
-call %java_exe% -Xmx512m -Djava.ext.dirs=%frameworkdir% -Dcom.android.monkeyrunner.bindir=..\framework -jar %jarpath% %*
+if not defined ANDROID_SWT goto QueryArch
+ set swt_path=%ANDROID_SWT%
+ goto SwtDone
+
+:QueryArch
+
+ for /f %%a in ('%java_exe% -jar %frameworkdir%archquery.jar') do set swt_path=%frameworkdir%%%a
+
+:SwtDone
+
+if exist %swt_path% goto SetPath
+ echo SWT folder '%swt_path%' does not exist.
+ echo Please set ANDROID_SWT to point to the folder containing swt.jar for your platform.
+ exit /B
+
+:SetPath
+
+call %java_exe% -Xmx512m -Djava.ext.dirs=%frameworkdir%;%swt_path% -Dcom.android.monkeyrunner.bindir=..\framework -jar %jarpath% %*
diff --git a/monkeyrunner/src/Android.mk b/monkeyrunner/src/Android.mk
index 38be272..8cca0bc 100644
--- a/monkeyrunner/src/Android.mk
+++ b/monkeyrunner/src/Android.mk
@@ -24,7 +24,9 @@ LOCAL_JAVA_LIBRARIES := \
jython \
guavalib \
jsilver \
- sdklib
+ sdklib \
+ hierarchyviewerlib \
+ swt
LOCAL_JAVA_RESOURCE_DIRS := resources
diff --git a/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java b/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java
index 864441e..badddff 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java
@@ -23,15 +23,18 @@ import java.text.BreakIterator;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.python.core.ArgParser;
import org.python.core.ClassDictInit;
import org.python.core.Py;
+import org.python.core.PyBoolean;
import org.python.core.PyDictionary;
import org.python.core.PyFloat;
import org.python.core.PyInteger;
@@ -73,6 +76,7 @@ public final class JythonUtils {
// What python calls float, most people call double
builder.put(PyFloat.class, Double.class);
builder.put(PyInteger.class, Integer.class);
+ builder.put(PyBoolean.class, Boolean.class);
PYOBJECT_TO_JAVA_OBJECT_MAP = builder.build();
}
@@ -228,6 +232,8 @@ public final class JythonUtils {
} else if (o instanceof Float) {
float f = (Float) o;
return new PyFloat(f);
+ } else if (o instanceof Boolean) {
+ return new PyBoolean((Boolean) o);
}
return Py.None;
}
@@ -479,4 +485,20 @@ public final class JythonUtils {
lines.add(currentLine.toString());
return lines;
}
+
+ /**
+ * Obtain the set of method names available from Python.
+ *
+ * @param clazz Class to inspect.
+ * @return set of method names annotated with {@code MonkeyRunnerExported}.
+ */
+ public static Set<String> getMethodNames(Class<?> clazz) {
+ HashSet<String> methodNames = new HashSet<String>();
+ for (Method m: clazz.getMethods()) {
+ if (m.isAnnotationPresent(MonkeyRunnerExported.class)) {
+ methodNames.add(m.getName());
+ }
+ }
+ return methodNames;
+ }
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java
index 0f4362a..649e33c 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java
@@ -20,8 +20,6 @@ import java.util.Collections;
import java.util.Map;
import java.util.Set;
-import javax.annotation.Nullable;
-
import org.python.core.ArgParser;
import org.python.core.ClassDictInit;
import org.python.core.Py;
@@ -30,7 +28,11 @@ import org.python.core.PyException;
import org.python.core.PyObject;
import org.python.core.PyTuple;
+import com.android.monkeyrunner.core.IMonkeyDevice;
+import com.android.monkeyrunner.core.IMonkeyImage;
import com.android.monkeyrunner.doc.MonkeyRunnerExported;
+import com.android.monkeyrunner.easy.HierarchyViewer;
+
import com.google.common.base.Functions;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
@@ -43,7 +45,7 @@ import com.google.common.collect.ImmutableMap;
* implementation of this class.
*/
@MonkeyRunnerExported(doc = "Represents a device attached to the system.")
-public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
+public class MonkeyDevice extends PyObject implements ClassDictInit {
public static void classDictInit(PyObject dict) {
JythonUtils.convertDocAnnotationsForClass(MonkeyDevice.class, dict);
}
@@ -55,37 +57,41 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
@MonkeyRunnerExported(doc = "Sends a DOWN event, immediately followed by an UP event when used with touch() or press()")
public static final String DOWN_AND_UP = "downAndUp";
+ // TODO: This may not be accessible from jython; if so, remove it.
public enum TouchPressType {
DOWN, UP, DOWN_AND_UP,
}
- public static final Map<String, TouchPressType> TOUCH_NAME_TO_ENUM =
- ImmutableMap.of(MonkeyDevice.DOWN, TouchPressType.DOWN,
- MonkeyDevice.UP, TouchPressType.UP,
- MonkeyDevice.DOWN_AND_UP, TouchPressType.DOWN_AND_UP);
+ public static final Map<String, IMonkeyDevice.TouchPressType> TOUCH_NAME_TO_ENUM =
+ ImmutableMap.of(DOWN, IMonkeyDevice.TouchPressType.DOWN,
+ UP, IMonkeyDevice.TouchPressType.UP,
+ DOWN_AND_UP, IMonkeyDevice.TouchPressType.DOWN_AND_UP);
private static final Set<String> VALID_DOWN_UP_TYPES = TOUCH_NAME_TO_ENUM.keySet();
- /**
- * Create a MonkeyMananger for talking to this device.
- *
- * NOTE: This is not part of the jython API.
- *
- * @return the MonkeyManager
- */
- public abstract MonkeyManager getManager();
-
- /**
- * Dispose of any native resoureces this device may have taken hold of.
- *
- * NOTE: This is not part of the jython API.
- */
- public abstract void dispose();
+ private IMonkeyDevice impl;
+
+ public MonkeyDevice(IMonkeyDevice impl) {
+ this.impl = impl;
+ }
+
+ public IMonkeyDevice getImpl() {
+ return impl;
+ }
+
+ @MonkeyRunnerExported(doc = "Get the HierarchyViewer object for the device.",
+ returns = "A HierarchyViewer object")
+ public HierarchyViewer getHierarchyViewer(PyObject[] args, String[] kws) {
+ return impl.getHierarchyViewer();
+ }
@MonkeyRunnerExported(doc =
"Gets the device's screen buffer, yielding a screen capture of the entire display.",
returns = "A MonkeyImage object (a bitmap wrapper)")
- public abstract MonkeyImage takeSnapshot();
+ public MonkeyImage takeSnapshot() {
+ IMonkeyImage image = impl.takeSnapshot();
+ return new MonkeyImage(image);
+ }
@MonkeyRunnerExported(doc = "Given the name of a variable on the device, " +
"returns the variable's value",
@@ -97,7 +103,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
ArgParser ap = JythonUtils.createArgParser(args, kws);
Preconditions.checkNotNull(ap);
- return getProperty(ap.getString(0));
+ return impl.getProperty(ap.getString(0));
}
@MonkeyRunnerExported(doc = "Synonym for getProperty()",
@@ -107,7 +113,8 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
public String getSystemProperty(PyObject[] args, String[] kws) {
ArgParser ap = JythonUtils.createArgParser(args, kws);
Preconditions.checkNotNull(ap);
- return getSystemProperty(ap.getString(0));
+
+ return impl.getSystemProperty(ap.getString(0));
}
@MonkeyRunnerExported(doc = "Sends a touch event at the specified location",
@@ -136,7 +143,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
// bad stuff was passed in, just use the already specified default value
type = MonkeyDevice.DOWN_AND_UP;
}
- touch(x, y, TOUCH_NAME_TO_ENUM.get(type));
+ impl.touch(x, y, TOUCH_NAME_TO_ENUM.get(type));
}
@MonkeyRunnerExported(doc = "Simulates dragging (touch, hold, and move) on the device screen.",
@@ -171,7 +178,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
int steps = ap.getInt(3, 10);
- drag(startx, starty, endx, endy, steps, ms);
+ impl.drag(startx, starty, endx, endy, steps, ms);
}
@MonkeyRunnerExported(doc = "Send a key event to the specified key",
@@ -199,7 +206,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
// bad stuff was passed in, just use the already specified default value
type = MonkeyDevice.DOWN_AND_UP;
}
- press(name, TOUCH_NAME_TO_ENUM.get(type));
+ impl.press(name, TOUCH_NAME_TO_ENUM.get(type));
}
@MonkeyRunnerExported(doc = "Types the specified string on the keyboard. This is " +
@@ -211,7 +218,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
Preconditions.checkNotNull(ap);
String message = ap.getString(0);
- type(message);
+ impl.type(message);
}
@MonkeyRunnerExported(doc = "Executes an adb shell command and returns the result, if any.",
@@ -223,7 +230,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
Preconditions.checkNotNull(ap);
String cmd = ap.getString(0);
- return shell(cmd);
+ return impl.shell(cmd);
}
@MonkeyRunnerExported(doc = "Reboots the specified device into a specified bootloader.",
@@ -235,7 +242,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
String into = ap.getString(0, null);
- reboot(into);
+ impl.reboot(into);
}
@MonkeyRunnerExported(doc = "Installs the specified Android package (.apk file) " +
@@ -248,7 +255,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
Preconditions.checkNotNull(ap);
String path = ap.getString(0);
- return installPackage(path);
+ return impl.installPackage(path);
}
@MonkeyRunnerExported(doc = "Deletes the specified package from the device, including its " +
@@ -261,7 +268,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
Preconditions.checkNotNull(ap);
String packageName = ap.getString(0);
- return removePackage(packageName);
+ return impl.removePackage(packageName);
}
@MonkeyRunnerExported(doc = "Starts an Activity on the device by sending an Intent " +
@@ -294,7 +301,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
String component = ap.getString(6, null);
int flags = ap.getInt(7, 0);
- startActivity(uri, action, data, mimetype, categories, extras, component, flags);
+ impl.startActivity(uri, action, data, mimetype, categories, extras, component, flags);
}
@MonkeyRunnerExported(doc = "Sends a broadcast intent to the device.",
@@ -326,7 +333,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
String component = ap.getString(6, null);
int flags = ap.getInt(7, 0);
- broadcastIntent(uri, action, data, mimetype, categories, extras, component, flags);
+ impl.broadcastIntent(uri, action, data, mimetype, categories, extras, component, flags);
}
@MonkeyRunnerExported(doc = "Run the specified package with instrumentation and return " +
@@ -354,7 +361,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
instrumentArgs = Collections.emptyMap();
}
- Map<String, Object> result = instrument(packageName, instrumentArgs);
+ Map<String, Object> result = impl.instrument(packageName, instrumentArgs);
return JythonUtils.convertMapToDict(result);
}
@@ -363,34 +370,6 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
ArgParser ap = JythonUtils.createArgParser(args, kws);
Preconditions.checkNotNull(ap);
- wake();
+ impl.wake();
}
-
- /**
- * Reboot the device.
- *
- * @param into which bootloader to boot into. Null means default reboot.
- */
- public abstract void reboot(@Nullable String into);
-
- public abstract String getProperty(String key);
- public abstract String getSystemProperty(String key);
- public abstract void touch(int x, int y, TouchPressType type);
- public abstract void press(String keyName, TouchPressType type);
- public abstract void drag(int startx, int starty, int endx, int endy, int steps, long ms);
- public abstract void type(String string);
- public abstract String shell(String cmd);
- public abstract boolean installPackage(String path);
- public abstract boolean removePackage(String packageName);
- public abstract void startActivity(@Nullable String uri, @Nullable String action,
- @Nullable String data, @Nullable String mimetype,
- Collection<String> categories, Map<String, Object> extras, @Nullable String component,
- int flags);
- public abstract void broadcastIntent(@Nullable String uri, @Nullable String action,
- @Nullable String data, @Nullable String mimetype,
- Collection<String> categories, Map<String, Object> extras, @Nullable String component,
- int flags);
- public abstract Map<String, Object> instrument(String packageName,
- Map<String, Object> args);
- public abstract void wake();
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyImage.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyImage.java
index d506613..b55b4f3 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyImage.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyImage.java
@@ -17,6 +17,7 @@ package com.android.monkeyrunner;
import com.google.common.base.Preconditions;
+import com.android.monkeyrunner.core.IMonkeyImage;
import com.android.monkeyrunner.doc.MonkeyRunnerExported;
import org.python.core.ArgParser;
@@ -25,57 +26,31 @@ import org.python.core.PyInteger;
import org.python.core.PyObject;
import org.python.core.PyTuple;
-import java.awt.Graphics;
import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.lang.ref.WeakReference;
-import java.util.Iterator;
-
-import javax.imageio.ImageIO;
-import javax.imageio.ImageWriter;
-import javax.imageio.stream.ImageOutputStream;
+import java.util.logging.Logger;
/**
* Jython object to encapsulate images that have been taken.
*/
@MonkeyRunnerExported(doc = "An image")
-public abstract class MonkeyImage extends PyObject implements ClassDictInit {
+public class MonkeyImage extends PyObject implements ClassDictInit {
+ private static Logger LOG = Logger.getLogger(MonkeyImage.class.getCanonicalName());
+
public static void classDictInit(PyObject dict) {
JythonUtils.convertDocAnnotationsForClass(MonkeyImage.class, dict);
}
- /**
- * Convert the MonkeyImage into a BufferedImage.
- *
- * @return a BufferedImage for this MonkeyImage.
- */
- public abstract BufferedImage createBufferedImage();
+ private IMonkeyImage impl;
- // Cache the BufferedImage so we don't have to generate it every time.
- private WeakReference<BufferedImage> cachedBufferedImage = null;
-
- /**
- * Utility method to handle getting the BufferedImage and managing the cache.
- *
- * @return the BufferedImage for this image.
- */
- private BufferedImage getBufferedImage() {
- // Check the cache first
- if (cachedBufferedImage != null) {
- BufferedImage img = cachedBufferedImage.get();
- if (img != null) {
- return img;
- }
- }
+ public MonkeyImage(IMonkeyImage impl) {
+ this.impl = impl;
+ }
- // Not in the cache, so create it and cache it.
- BufferedImage img = createBufferedImage();
- cachedBufferedImage = new WeakReference<BufferedImage>(img);
- return img;
+ public IMonkeyImage getImpl() {
+ return impl;
}
+
@MonkeyRunnerExported(doc = "Converts the MonkeyImage into a particular format and returns " +
"the result as a String. Use this to get access to the raw" +
"pixels in a particular format. String output is for better " +
@@ -89,16 +64,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit {
Preconditions.checkNotNull(ap);
String format = ap.getString(0, "png");
-
- BufferedImage argb = convertSnapshot();
-
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- try {
- ImageIO.write(argb, format, os);
- } catch (IOException e) {
- return new byte[0];
- }
- return os.toByteArray();
+ return impl.convertToBytes(format);
}
@MonkeyRunnerExported(doc = "Write the MonkeyImage to a file. If no " +
@@ -116,38 +82,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit {
String path = ap.getString(0);
String format = ap.getString(1, null);
-
- if (format != null) {
- return writeToFile(path, format);
- }
- int offset = path.lastIndexOf('.');
- if (offset < 0) {
- return writeToFile(path, "png");
- }
- String ext = path.substring(offset + 1);
- Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix(ext);
- if (!writers.hasNext()) {
- return writeToFile(path, "png");
- }
- ImageWriter writer = writers.next();
- BufferedImage image = getBufferedImage();
- try {
- File f = new File(path);
- f.delete();
-
- ImageOutputStream outputStream = ImageIO.createImageOutputStream(f);
- writer.setOutput(outputStream);
-
- try {
- writer.write(image);
- } finally {
- writer.dispose();
- outputStream.flush();
- }
- } catch (IOException e) {
- return false;
- }
- return true;
+ return impl.writeToFile(path, format);
}
@MonkeyRunnerExported(doc = "Get a single ARGB (alpha, red, green, blue) pixel at location " +
@@ -163,7 +98,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit {
int x = ap.getInt(0);
int y = ap.getInt(1);
- int pixel = getPixel(x, y);
+ int pixel = impl.getPixel(x, y);
PyInteger a = new PyInteger((pixel & 0xFF000000) >> 24);
PyInteger r = new PyInteger((pixel & 0x00FF0000) >> 16);
PyInteger g = new PyInteger((pixel & 0x0000FF00) >> 8);
@@ -184,35 +119,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit {
int x = ap.getInt(0);
int y = ap.getInt(1);
- return getPixel(x, y);
- }
-
- private int getPixel(int x, int y) {
- BufferedImage image = getBufferedImage();
- return image.getRGB(x, y);
- }
-
- private BufferedImage convertSnapshot() {
- BufferedImage image = getBufferedImage();
-
- // Convert the image to ARGB so ImageIO writes it out nicely
- BufferedImage argb = new BufferedImage(image.getWidth(), image.getHeight(),
- BufferedImage.TYPE_INT_ARGB);
- Graphics g = argb.createGraphics();
- g.drawImage(image, 0, 0, null);
- g.dispose();
- return argb;
- }
-
- public boolean writeToFile(String path, String format) {
- BufferedImage argb = convertSnapshot();
-
- try {
- ImageIO.write(argb, format, new File(path));
- } catch (IOException e) {
- return false;
- }
- return true;
+ return impl.getPixel(x, y);
}
@MonkeyRunnerExported(doc = "Compare this MonkeyImage object to aother MonkeyImage object.",
@@ -227,53 +134,13 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit {
Preconditions.checkNotNull(ap);
PyObject otherObject = ap.getPyObject(0);
- MonkeyImage other = (MonkeyImage) otherObject.__tojava__(MonkeyImage.class);
+ // TODO: check if this conversion wortks
+ IMonkeyImage other = (IMonkeyImage) otherObject.__tojava__(
+ IMonkeyImage.class);
double percent = JythonUtils.getFloat(ap, 1, 1.0);
- BufferedImage otherImage = other.getBufferedImage();
- BufferedImage myImage = getBufferedImage();
-
- // Easy size check
- if (otherImage.getWidth() != myImage.getWidth()) {
- return false;
- }
- if (otherImage.getHeight() != myImage.getHeight()) {
- return false;
- }
-
- int[] otherPixel = new int[1];
- int[] myPixel = new int[1];
-
- int width = myImage.getWidth();
- int height = myImage.getHeight();
-
- int numDiffPixels = 0;
- // Now, go through pixel-by-pixel and check that the images are the same;
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- if (myImage.getRGB(x, y) != otherImage.getRGB(x, y)) {
- numDiffPixels++;
- }
- }
- }
- double numberPixels = (height * width);
- double diffPercent = numDiffPixels / numberPixels;
- return percent <= 1.0 - diffPercent;
- }
-
- private static class BufferedImageMonkeyImage extends MonkeyImage {
- private final BufferedImage image;
-
- public BufferedImageMonkeyImage(BufferedImage image) {
- this.image = image;
- }
-
- @Override
- public BufferedImage createBufferedImage() {
- return image;
- }
-
+ return impl.sameAs(other, percent);
}
@MonkeyRunnerExported(doc = "Copy a rectangular region of the image.",
@@ -292,7 +159,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit {
int w = rect.__getitem__(2).asInt();
int h = rect.__getitem__(3).asInt();
- BufferedImage image = getBufferedImage();
- return new BufferedImageMonkeyImage(image.getSubimage(x, y, w, h));
+ IMonkeyImage image = impl.getSubImage(x, y, w, h);
+ return new MonkeyImage(image);
}
-}
+} \ No newline at end of file
diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
index 8480223..5529802 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
@@ -19,6 +19,10 @@ import com.google.common.base.Functions;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
+import com.android.monkeyrunner.core.IMonkeyBackend;
+import com.android.monkeyrunner.core.IMonkeyDevice;
+import com.android.monkeyrunner.core.IMonkeyImage;
+import com.android.monkeyrunner.core.MonkeyImageBase;
import com.android.monkeyrunner.doc.MonkeyRunnerExported;
import org.python.core.ArgParser;
@@ -38,7 +42,7 @@ import javax.swing.JOptionPane;
@MonkeyRunnerExported(doc = "Main entry point for MonkeyRunner")
public class MonkeyRunner extends PyObject implements ClassDictInit {
private static final Logger LOG = Logger.getLogger(MonkeyRunner.class.getCanonicalName());
- private static MonkeyRunnerBackend backend;
+ private static IMonkeyBackend backend;
public static void classDictInit(PyObject dict) {
JythonUtils.convertDocAnnotationsForClass(MonkeyRunner.class, dict);
@@ -49,7 +53,7 @@ public class MonkeyRunner extends PyObject implements ClassDictInit {
*
* @param backend the backend to use.
*/
- /* package */ static void setBackend(MonkeyRunnerBackend backend) {
+ /* package */ static void setBackend(IMonkeyBackend backend) {
MonkeyRunner.backend = backend;
}
@@ -71,8 +75,10 @@ public class MonkeyRunner extends PyObject implements ClassDictInit {
timeoutMs = Long.MAX_VALUE;
}
- return backend.waitForConnection(timeoutMs,
+ IMonkeyDevice device = backend.waitForConnection(timeoutMs,
ap.getString(1, ".*"));
+ MonkeyDevice monkeyDevice = new MonkeyDevice(device);
+ return monkeyDevice;
}
@MonkeyRunnerExported(doc = "Pause the currently running program for the specified " +
@@ -174,6 +180,21 @@ public class MonkeyRunner extends PyObject implements ClassDictInit {
return choice(message, title, choices);
}
+ @MonkeyRunnerExported(doc = "Loads a MonkeyImage from a file.",
+ args = { "path" },
+ argDocs = {
+ "The path to the file to load. This file path is in terms of the computer running " +
+ "MonkeyRunner and not a path on the Android Device. " },
+ returns = "A new MonkeyImage representing the specified file")
+ public static MonkeyImage loadImageFromFile(PyObject[] args, String kws[]) {
+ ArgParser ap = JythonUtils.createArgParser(args, kws);
+ Preconditions.checkNotNull(ap);
+
+ String path = ap.getString(0);
+ IMonkeyImage image = MonkeyImageBase.loadImageFromFile(path);
+ return new MonkeyImage(image);
+ }
+
/**
* Display an alert dialog.
*
diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerStarter.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerStarter.java
index 90fce6f..8c9942c 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerStarter.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerStarter.java
@@ -20,6 +20,7 @@ import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.android.monkeyrunner.adb.AdbBackend;
+import com.android.monkeyrunner.core.IMonkeyBackend;
import com.android.monkeyrunner.stub.StubBackend;
import org.python.util.PythonInterpreter;
@@ -50,7 +51,7 @@ public class MonkeyRunnerStarter {
private static final Logger LOG = Logger.getLogger(MonkeyRunnerStarter.class.getName());
private static final String MONKEY_RUNNER_MAIN_MANIFEST_NAME = "MonkeyRunnerStartupRunner";
- private final MonkeyRunnerBackend backend;
+ private final IMonkeyBackend backend;
private final MonkeyRunnerOptions options;
public MonkeyRunnerStarter(MonkeyRunnerOptions options) {
@@ -68,7 +69,7 @@ public class MonkeyRunnerStarter {
* @param backendName the name of the backend to create
* @return the new backend, or null if none were found.
*/
- public static MonkeyRunnerBackend createBackendByName(String backendName) {
+ public static IMonkeyBackend createBackendByName(String backendName) {
if ("adb".equals(backendName)) {
return new AdbBackend();
} else if ("stub".equals(backendName)) {
@@ -191,13 +192,12 @@ public class MonkeyRunnerStarter {
public static void main(String[] args) {
MonkeyRunnerOptions options = MonkeyRunnerOptions.processOptions(args);
- // logging property files are difficult
- replaceAllLogFormatters(MonkeyFormatter.DEFAULT_INSTANCE, options.getLogLevel());
-
if (options == null) {
return;
}
+ // logging property files are difficult
+ replaceAllLogFormatters(MonkeyFormatter.DEFAULT_INSTANCE, options.getLogLevel());
MonkeyRunnerStarter runner = new MonkeyRunnerStarter(options);
int error = runner.run();
diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbBackend.java b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbBackend.java
index 455d131..49cac08 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbBackend.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbBackend.java
@@ -19,8 +19,8 @@ import com.google.common.collect.Lists;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.IDevice;
-import com.android.monkeyrunner.MonkeyDevice;
-import com.android.monkeyrunner.MonkeyRunnerBackend;
+import com.android.monkeyrunner.core.IMonkeyBackend;
+import com.android.monkeyrunner.core.IMonkeyDevice;
import com.android.sdklib.SdkConstants;
import java.io.File;
@@ -32,12 +32,11 @@ import java.util.regex.Pattern;
/**
* Backend implementation that works over ADB to talk to the device.
*/
-public class AdbBackend implements MonkeyRunnerBackend {
+public class AdbBackend implements IMonkeyBackend {
private static Logger LOG = Logger.getLogger(AdbBackend.class.getCanonicalName());
// How long to wait each time we check for the device to be connected.
private static final int CONNECTION_ITERATION_TIMEOUT_MS = 200;
- private final List<AdbMonkeyDevice> devices = Lists.newArrayList();
-
+ private final List<IMonkeyDevice> devices = Lists.newArrayList();
private final AndroidDebugBridge bridge;
public AdbBackend() {
@@ -87,18 +86,20 @@ public class AdbBackend implements MonkeyRunnerBackend {
return null;
}
- public MonkeyDevice waitForConnection() {
+ @Override
+ public IMonkeyDevice waitForConnection() {
return waitForConnection(Integer.MAX_VALUE, ".*");
}
- public MonkeyDevice waitForConnection(long timeoutMs, String deviceIdRegex) {
+ @Override
+ public IMonkeyDevice waitForConnection(long timeoutMs, String deviceIdRegex) {
do {
IDevice device = findAttacedDevice(deviceIdRegex);
// Only return the device when it is online
if (device != null && device.getState() == IDevice.DeviceState.ONLINE) {
- AdbMonkeyDevice amd = new AdbMonkeyDevice(device);
- devices.add(amd);
- return amd;
+ IMonkeyDevice monkeyDevice = new AdbMonkeyDevice(device);
+ devices.add(monkeyDevice);
+ return monkeyDevice;
}
try {
@@ -113,8 +114,9 @@ public class AdbBackend implements MonkeyRunnerBackend {
return null;
}
+ @Override
public void shutdown() {
- for (AdbMonkeyDevice device : devices) {
+ for (IMonkeyDevice device : devices) {
device.dispose();
}
AndroidDebugBridge.terminate();
diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java
index e7e2e1c..60eaba9 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java
@@ -25,10 +25,11 @@ import com.android.ddmlib.IDevice;
import com.android.ddmlib.InstallException;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.TimeoutException;
-import com.android.monkeyrunner.MonkeyDevice;
-import com.android.monkeyrunner.MonkeyImage;
import com.android.monkeyrunner.MonkeyManager;
import com.android.monkeyrunner.adb.LinearInterpolator.Point;
+import com.android.monkeyrunner.core.IMonkeyImage;
+import com.android.monkeyrunner.core.IMonkeyDevice;
+import com.android.monkeyrunner.easy.HierarchyViewer;
import java.io.IOException;
import java.net.InetAddress;
@@ -47,7 +48,7 @@ import java.util.regex.Pattern;
import javax.annotation.Nullable;
-public class AdbMonkeyDevice extends MonkeyDevice {
+public class AdbMonkeyDevice implements IMonkeyDevice {
private static final Logger LOG = Logger.getLogger(AdbMonkeyDevice.class.getName());
private static final String[] ZERO_LENGTH_STRING_ARRAY = new String[0];
@@ -81,9 +82,15 @@ public class AdbMonkeyDevice extends MonkeyDevice {
manager = null;
}
+ @Override
+ public HierarchyViewer getHierarchyViewer() {
+ return new HierarchyViewer(device);
+ }
+
private void executeAsyncCommand(final String command,
final LoggingOutputReceiver logger) {
executor.submit(new Runnable() {
+ @Override
public void run() {
try {
device.executeShellCommand(command, logger);
@@ -184,7 +191,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- public MonkeyImage takeSnapshot() {
+ public IMonkeyImage takeSnapshot() {
try {
return new AdbMonkeyImage(device.getScreenshot());
} catch (TimeoutException e) {
@@ -419,9 +426,10 @@ public class AdbMonkeyDevice extends MonkeyDevice {
} else {
// treat is as a string.
valueString = value.toString();
- arg = "--esmake";
+ arg = "--es";
}
parts.add(arg);
+ parts.add(entry.getKey());
parts.add(valueString);
}
@@ -498,6 +506,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
LinearInterpolator.Point start = new LinearInterpolator.Point(startx, starty);
LinearInterpolator.Point end = new LinearInterpolator.Point(endx, endy);
lerp.interpolate(start, end, new LinearInterpolator.Callback() {
+ @Override
public void step(Point point) {
try {
manager.touchMove(point.getX(), point.getY());
@@ -512,9 +521,11 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
}
+ @Override
public void start(Point point) {
try {
manager.touchDown(point.getX(), point.getY());
+ manager.touchMove(point.getX(), point.getY());
} catch (IOException e) {
LOG.log(Level.SEVERE, "Error sending drag start event", e);
}
@@ -526,8 +537,10 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
}
+ @Override
public void end(Point point) {
try {
+ manager.touchMove(point.getX(), point.getY());
manager.touchUp(point.getX(), point.getY());
} catch (IOException e) {
LOG.log(Level.SEVERE, "Error sending drag end event", e);
diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyImage.java b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyImage.java
index fc32600..e2bd86e 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyImage.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyImage.java
@@ -16,15 +16,15 @@
package com.android.monkeyrunner.adb;
import com.android.ddmlib.RawImage;
-import com.android.monkeyrunner.MonkeyImage;
import com.android.monkeyrunner.adb.image.ImageUtils;
+import com.android.monkeyrunner.core.MonkeyImageBase;
import java.awt.image.BufferedImage;
/**
* ADB implementation of the MonkeyImage class.
*/
-public class AdbMonkeyImage extends MonkeyImage {
+public class AdbMonkeyImage extends MonkeyImageBase {
private final RawImage image;
/**
diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/image/CaptureRawAndConvertedImage.java b/monkeyrunner/src/com/android/monkeyrunner/adb/image/CaptureRawAndConvertedImage.java
index 7e31ea5..5a317f1 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/adb/image/CaptureRawAndConvertedImage.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/adb/image/CaptureRawAndConvertedImage.java
@@ -16,9 +16,11 @@
package com.android.monkeyrunner.adb.image;
import com.android.ddmlib.RawImage;
-import com.android.monkeyrunner.MonkeyDevice;
import com.android.monkeyrunner.adb.AdbBackend;
import com.android.monkeyrunner.adb.AdbMonkeyImage;
+import com.android.monkeyrunner.core.IMonkeyBackend;
+import com.android.monkeyrunner.core.IMonkeyImage;
+import com.android.monkeyrunner.core.IMonkeyDevice;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -94,13 +96,13 @@ public class CaptureRawAndConvertedImage {
}
public static void main(String[] args) throws IOException {
- AdbBackend backend = new AdbBackend();
- MonkeyDevice device = backend.waitForConnection();
- AdbMonkeyImage snapshot = (AdbMonkeyImage) device.takeSnapshot();
+ IMonkeyBackend backend = new AdbBackend();
+ IMonkeyDevice device = backend.waitForConnection();
+ IMonkeyImage snapshot = (IMonkeyImage) device.takeSnapshot();
// write out to a file
snapshot.writeToFile("output.png", "png");
- writeOutImage(snapshot.getRawImage(), "output.raw");
+ writeOutImage(((AdbMonkeyImage)snapshot).getRawImage(), "output.raw");
System.exit(0);
}
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyController.java b/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyController.java
index e199a75..ca3195c 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyController.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyController.java
@@ -15,8 +15,9 @@
*/
package com.android.monkeyrunner.controller;
-import com.android.monkeyrunner.MonkeyDevice;
import com.android.monkeyrunner.adb.AdbBackend;
+import com.android.monkeyrunner.core.IMonkeyBackend;
+import com.android.monkeyrunner.core.IMonkeyDevice;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
@@ -36,9 +37,10 @@ public class MonkeyController extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
+ @Override
public void run() {
- AdbBackend adb = new AdbBackend();
- final MonkeyDevice device = adb.waitForConnection();
+ IMonkeyBackend adb = new AdbBackend();
+ final IMonkeyDevice device = adb.waitForConnection();
MonkeyControllerFrame mf = new MonkeyControllerFrame(device);
mf.setVisible(true);
mf.addWindowListener(new WindowAdapter() {
diff --git a/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyControllerFrame.java b/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyControllerFrame.java
index 7f5a7d8..7750936 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyControllerFrame.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyControllerFrame.java
@@ -15,10 +15,10 @@
*/
package com.android.monkeyrunner.controller;
-import com.android.monkeyrunner.MonkeyDevice;
-import com.android.monkeyrunner.MonkeyImage;
import com.android.monkeyrunner.MonkeyManager;
import com.android.monkeyrunner.PhysicalButton;
+import com.android.monkeyrunner.core.IMonkeyImage;
+import com.android.monkeyrunner.core.IMonkeyDevice;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
@@ -61,7 +61,7 @@ public class MonkeyControllerFrame extends JFrame {
}
});
- private final MonkeyDevice device;
+ private final IMonkeyDevice device;
private class PressAction extends AbstractAction {
private final PhysicalButton button;
@@ -85,7 +85,7 @@ public class MonkeyControllerFrame extends JFrame {
return button;
}
- public MonkeyControllerFrame(MonkeyDevice device) {
+ public MonkeyControllerFrame(IMonkeyDevice device) {
super("MonkeyController");
this.device = device;
@@ -155,7 +155,7 @@ public class MonkeyControllerFrame extends JFrame {
}
private void updateScreen() {
- MonkeyImage snapshot = device.takeSnapshot();
+ IMonkeyImage snapshot = device.takeSnapshot();
currentImage = snapshot.createBufferedImage();
imageLabel.setIcon(new ImageIcon(currentImage));
diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerBackend.java b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyBackend.java
index 216d214..3c1b943 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerBackend.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyBackend.java
@@ -13,13 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.monkeyrunner;
+package com.android.monkeyrunner.core;
+
+import com.android.monkeyrunner.MonkeyDevice;
/**
* Interface between MonkeyRunner common code and the MonkeyRunner backend. The backend is
* responsible for communicating between the host and the device.
*/
-public interface MonkeyRunnerBackend {
+public interface IMonkeyBackend {
+ /**
+ * Wait for a default device to connect to the backend.
+ *
+ * @return the connected device (or null if timeout);
+ */
+ IMonkeyDevice waitForConnection();
+
/**
* Wait for a device to connect to the backend.
*
@@ -27,7 +36,7 @@ public interface MonkeyRunnerBackend {
* @param deviceIdRegex the regular expression to specify which device to wait for.
* @return the connected device (or null if timeout);
*/
- MonkeyDevice waitForConnection(long timeoutMs, String deviceIdRegex);
+ IMonkeyDevice waitForConnection(long timeoutMs, String deviceIdRegex);
/**
* Shutdown the backend and cleanup any resources it was using.
diff --git a/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyDevice.java
new file mode 100644
index 0000000..c081a56
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyDevice.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2011 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.core;
+
+import com.android.monkeyrunner.MonkeyManager;
+import com.android.monkeyrunner.easy.HierarchyViewer;
+
+import java.util.Collection;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+/**
+ * MonkeyDevice interface.
+ */
+public interface IMonkeyDevice {
+ enum TouchPressType {
+ DOWN, UP, DOWN_AND_UP,
+ }
+
+ /**
+ * Create a MonkeyMananger for talking to this device.
+ *
+ * @return the MonkeyManager
+ */
+ MonkeyManager getManager();
+
+ /**
+ * Dispose of any native resources this device may have taken hold of.
+ */
+ void dispose();
+
+ /**
+ * @return hierarchy viewer implementation for querying state of the view
+ * hierarchy.
+ */
+ HierarchyViewer getHierarchyViewer();
+
+ /**
+ * Take the current screen's snapshot.
+ * @return the snapshot image
+ */
+ IMonkeyImage takeSnapshot();
+
+ /**
+ * Reboot the device.
+ *
+ * @param into which bootloader to boot into. Null means default reboot.
+ */
+ void reboot(@Nullable String into);
+
+ /**
+ * Get device's property.
+ *
+ * @param key the property name
+ * @return the property value
+ */
+ String getProperty(String key);
+
+ /**
+ * Get system property.
+ *
+ * @param key the name of the system property
+ * @return the property value
+ */
+ String getSystemProperty(String key);
+
+ /**
+ * Perform a touch of the given type at (x,y).
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param type the touch type
+ */
+ void touch(int x, int y, TouchPressType type);
+
+ /**
+ * Perform a press of a given type using a given key.
+ *
+ * TODO: define standard key names in a separate class or enum
+ *
+ * @param keyName the name of the key to use
+ * @param type the type of press to perform
+ */
+ void press(String keyName, TouchPressType type);
+
+ /**
+ * Perform a drag from one one location to another
+ *
+ * @param startx the x coordinate of the drag's starting point
+ * @param starty the y coordinate of the drag's starting point
+ * @param endx the x coordinate of the drag's end point
+ * @param endy the y coordinate of the drag's end point
+ * @param steps the number of steps to take when interpolating points
+ * @param ms the duration of the drag
+ */
+ void drag(int startx, int starty, int endx, int endy, int steps, long ms);
+
+ /**
+ * Type a given string.
+ *
+ * @param string the string to type
+ */
+ void type(String string);
+
+ /**
+ * Execute a shell command.
+ *
+ * @param cmd the command to execute
+ * @return the output of the command
+ */
+ String shell(String cmd);
+
+ /**
+ * Install a given package.
+ *
+ * @param path the path to the installation package
+ * @return true if success
+ */
+ boolean installPackage(String path);
+
+ /**
+ * Uninstall a given package.
+ *
+ * @param packageName the name of the package
+ * @return true if success
+ */
+ boolean removePackage(String packageName);
+
+ /**
+ * Start an activity.
+ *
+ * @param uri the URI for the Intent
+ * @param action the action for the Intent
+ * @param data the data URI for the Intent
+ * @param mimeType the mime type for the Intent
+ * @param categories the category names for the Intent
+ * @param extras the extras to add to the Intent
+ * @param component the component of the Intent
+ * @param flags the flags for the Intent
+ */
+ void startActivity(@Nullable String uri, @Nullable String action,
+ @Nullable String data, @Nullable String mimeType,
+ Collection<String> categories, Map<String, Object> extras, @Nullable String component,
+ int flags);
+
+ /**
+ * Send a broadcast intent to the device.
+ *
+ * @param uri the URI for the Intent
+ * @param action the action for the Intent
+ * @param data the data URI for the Intent
+ * @param mimeType the mime type for the Intent
+ * @param categories the category names for the Intent
+ * @param extras the extras to add to the Intent
+ * @param component the component of the Intent
+ * @param flags the flags for the Intent
+ */
+ void broadcastIntent(@Nullable String uri, @Nullable String action,
+ @Nullable String data, @Nullable String mimeType,
+ Collection<String> categories, Map<String, Object> extras, @Nullable String component,
+ int flags);
+
+ /**
+ * Run the specified package with instrumentation and return the output it
+ * generates.
+ *
+ * Use this to run a test package using InstrumentationTestRunner.
+ *
+ * @param packageName The class to run with instrumentation. The format is
+ * packageName/className. Use packageName to specify the Android package to
+ * run, and className to specify the class to run within that package. For
+ * test packages, this is usually testPackageName/InstrumentationTestRunner
+ * @param args a map of strings to objects containing the arguments to pass
+ * to this instrumentation.
+ * @return A map of strings to objects for the output from the package.
+ * For a test package, contains a single key-value pair: the key is 'stream'
+ * and the value is a string containing the test output.
+ */
+ Map<String, Object> instrument(String packageName,
+ Map<String, Object> args);
+
+ /**
+ * Wake up the screen on the device.
+ */
+ void wake();
+}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyImage.java b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyImage.java
new file mode 100644
index 0000000..5a24fa7
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyImage.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 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.core;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * MonkeyImage interface.
+ *
+ * This interface defines an image representing a screen snapshot.
+ */
+public interface IMonkeyImage {
+ // TODO: add java docs
+ BufferedImage createBufferedImage();
+ BufferedImage getBufferedImage();
+
+ IMonkeyImage getSubImage(int x, int y, int w, int h);
+
+ byte[] convertToBytes(String format);
+ boolean writeToFile(String path, String format);
+ int getPixel(int x, int y);
+ boolean sameAs(IMonkeyImage other, double percent);
+}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/core/MonkeyImageBase.java b/monkeyrunner/src/com/android/monkeyrunner/core/MonkeyImageBase.java
new file mode 100644
index 0000000..04ccb93
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/core/MonkeyImageBase.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2011 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.core;
+
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriter;
+import javax.imageio.stream.ImageOutputStream;
+
+/**
+ * Base class with basic functionality for MonkeyImage implementations.
+ */
+public abstract class MonkeyImageBase implements IMonkeyImage {
+ private static Logger LOG = Logger.getLogger(MonkeyImageBase.class.getCanonicalName());
+
+ /**
+ * Convert the MonkeyImage to a BufferedImage.
+ *
+ * @return a BufferedImage for this MonkeyImage.
+ */
+ @Override
+ public abstract BufferedImage createBufferedImage();
+
+ // Cache the BufferedImage so we don't have to generate it every time.
+ private WeakReference<BufferedImage> cachedBufferedImage = null;
+
+ /**
+ * Utility method to handle getting the BufferedImage and managing the cache.
+ *
+ * @return the BufferedImage for this image.
+ */
+ @Override
+ public BufferedImage getBufferedImage() {
+ // Check the cache first
+ if (cachedBufferedImage != null) {
+ BufferedImage img = cachedBufferedImage.get();
+ if (img != null) {
+ return img;
+ }
+ }
+
+ // Not in the cache, so create it and cache it.
+ BufferedImage img = createBufferedImage();
+ cachedBufferedImage = new WeakReference<BufferedImage>(img);
+ return img;
+ }
+
+ @Override
+ public byte[] convertToBytes(String format) {
+ BufferedImage argb = convertSnapshot();
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ try {
+ ImageIO.write(argb, format, os);
+ } catch (IOException e) {
+ return new byte[0];
+ }
+ return os.toByteArray();
+ }
+
+ @Override
+ public boolean writeToFile(String path, String format) {
+ if (format != null) {
+ return writeToFileHelper(path, format);
+ }
+ int offset = path.lastIndexOf('.');
+ if (offset < 0) {
+ return writeToFileHelper(path, "png");
+ }
+ String ext = path.substring(offset + 1);
+ Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix(ext);
+ if (!writers.hasNext()) {
+ return writeToFileHelper(path, "png");
+ }
+ ImageWriter writer = writers.next();
+ BufferedImage image = convertSnapshot();
+ try {
+ File f = new File(path);
+ f.delete();
+
+ ImageOutputStream outputStream = ImageIO.createImageOutputStream(f);
+ writer.setOutput(outputStream);
+
+ try {
+ writer.write(image);
+ } finally {
+ writer.dispose();
+ outputStream.flush();
+ }
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int getPixel(int x, int y) {
+ BufferedImage image = getBufferedImage();
+ return image.getRGB(x, y);
+ }
+
+ private BufferedImage convertSnapshot() {
+ BufferedImage image = getBufferedImage();
+
+ // Convert the image to ARGB so ImageIO writes it out nicely
+ BufferedImage argb = new BufferedImage(image.getWidth(), image.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics g = argb.createGraphics();
+ g.drawImage(image, 0, 0, null);
+ g.dispose();
+ return argb;
+ }
+
+ private boolean writeToFileHelper(String path, String format) {
+ BufferedImage argb = convertSnapshot();
+
+ try {
+ ImageIO.write(argb, format, new File(path));
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean sameAs(IMonkeyImage other, double percent) {
+ BufferedImage otherImage = other.getBufferedImage();
+ BufferedImage myImage = getBufferedImage();
+
+ // Easy size check
+ if (otherImage.getWidth() != myImage.getWidth()) {
+ return false;
+ }
+ if (otherImage.getHeight() != myImage.getHeight()) {
+ return false;
+ }
+
+ int[] otherPixel = new int[1];
+ int[] myPixel = new int[1];
+
+ int width = myImage.getWidth();
+ int height = myImage.getHeight();
+
+ int numDiffPixels = 0;
+ // Now, go through pixel-by-pixel and check that the images are the same;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (myImage.getRGB(x, y) != otherImage.getRGB(x, y)) {
+ numDiffPixels++;
+ }
+ }
+ }
+ double numberPixels = (height * width);
+ double diffPercent = numDiffPixels / numberPixels;
+ return percent <= 1.0 - diffPercent;
+ }
+
+ // TODO: figure out the location of this class and is superclasses
+ private static class BufferedImageMonkeyImage extends MonkeyImageBase {
+ private final BufferedImage image;
+
+ public BufferedImageMonkeyImage(BufferedImage image) {
+ this.image = image;
+ }
+
+ @Override
+ public BufferedImage createBufferedImage() {
+ return image;
+ }
+ }
+
+ public static IMonkeyImage loadImageFromFile(String path) {
+ File f = new File(path);
+ if (f.exists() && f.canRead()) {
+ try {
+ BufferedImage bufferedImage = ImageIO.read(new File(path));
+ if (bufferedImage == null) {
+ LOG.log(Level.WARNING, "Cannot decode file %s", path);
+ return null;
+ }
+ return new BufferedImageMonkeyImage(bufferedImage);
+ } catch (IOException e) {
+ LOG.log(Level.WARNING, "Exception trying to decode image", e);
+ return null;
+ }
+ } else {
+ LOG.log(Level.WARNING, "Cannot read file %s", path);
+ return null;
+ }
+ }
+
+ @Override
+ public IMonkeyImage getSubImage(int x, int y, int w, int h) {
+ BufferedImage image = getBufferedImage();
+ return new BufferedImageMonkeyImage(image.getSubimage(x, y, w, h));
+ }
+}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/easy/By.java b/monkeyrunner/src/com/android/monkeyrunner/easy/By.java
new file mode 100644
index 0000000..1ed1c6f
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/easy/By.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 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.easy;
+
+import com.google.common.base.Preconditions;
+
+import com.android.hierarchyviewerlib.device.ViewNode;
+import com.android.monkeyrunner.JythonUtils;
+import com.android.monkeyrunner.doc.MonkeyRunnerExported;
+
+import org.python.core.ArgParser;
+import org.python.core.ClassDictInit;
+import org.python.core.PyObject;
+
+/**
+ * Select a view object based on some criteria.
+ *
+ * Currently supports the By.id criteria to search for an element by id.
+ * In the future it will support other criteria such as:
+ * By.classid - search by class.
+ * By.hash - search by hashcode
+ * and recursively searching under an already selected object.
+ *
+ * WARNING: This API is under development, expect the interface to change
+ * without notice.
+ *
+ * TODO: Implement other selectors, like classid, hash, and so on.
+ * TODO: separate java-only core from jython wrapper
+ */
+public class By extends PyObject implements ClassDictInit {
+ public static void classDictInit(PyObject dict) {
+ JythonUtils.convertDocAnnotationsForClass(By.class, dict);
+ }
+
+ private String id;
+
+ By(String id) {
+ this.id = id;
+ }
+
+ @MonkeyRunnerExported(doc = "Select an object by id.",
+ args = { "id" },
+ argDocs = { "The identifier of the object." })
+ public static By id(PyObject[] args, String[] kws) {
+ ArgParser ap = JythonUtils.createArgParser(args, kws);
+ Preconditions.checkNotNull(ap);
+
+ String id = ap.getString(0);
+ return new By(id);
+ }
+
+ public static By id(String id) {
+ return new By(id);
+ }
+
+ /**
+ * Find the selected view from the root view node.
+ */
+ ViewNode find(ViewNode rootNode) {
+ if (rootNode.id.equals(id)) {
+ return rootNode;
+ }
+ for (ViewNode child : rootNode.children) {
+ ViewNode found = find(child);
+ if (found != null) {
+ return found;
+ }
+ }
+ return null;
+ }
+}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/easy/EasyMonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/easy/EasyMonkeyDevice.java
new file mode 100644
index 0000000..e72e462
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/easy/EasyMonkeyDevice.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2011 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.easy;
+
+import com.google.common.base.Preconditions;
+
+import com.android.hierarchyviewerlib.device.ViewNode;
+import com.android.hierarchyviewerlib.device.ViewNode.Property;
+import com.android.monkeyrunner.JythonUtils;
+import com.android.monkeyrunner.MonkeyDevice;
+import com.android.monkeyrunner.core.IMonkeyDevice.TouchPressType;
+import com.android.monkeyrunner.doc.MonkeyRunnerExported;
+
+import org.eclipse.swt.graphics.Point;
+import org.python.core.ArgParser;
+import org.python.core.ClassDictInit;
+import org.python.core.Py;
+import org.python.core.PyException;
+import org.python.core.PyInteger;
+import org.python.core.PyObject;
+import org.python.core.PyString;
+import org.python.core.PyTuple;
+
+import java.util.Set;
+
+/**
+ * Extends {@link MonkeyDevice} to support looking up views using a 'selector'.
+ * Currently, only identifiers can be used as a selector. All methods on
+ * MonkeyDevice can be used on this class in Python.
+ *
+ * WARNING: This API is under development, expect the interface to change
+ * without notice.
+ */
+@MonkeyRunnerExported(doc = "MonkeyDevice with easier methods to refer to objects.")
+public class EasyMonkeyDevice extends PyObject implements ClassDictInit {
+ public static void classDictInit(PyObject dict) {
+ JythonUtils.convertDocAnnotationsForClass(EasyMonkeyDevice.class, dict);
+ }
+
+ private MonkeyDevice mDevice;
+ private HierarchyViewer mHierarchyViewer;
+
+ private static final Set<String> EXPORTED_METHODS =
+ JythonUtils.getMethodNames(EasyMonkeyDevice.class);
+
+ @MonkeyRunnerExported(doc = "Creates EasyMonkeyDevice with an underlying MonkeyDevice.",
+ args = { "device" },
+ argDocs = { "MonkeyDevice to extend." })
+ public EasyMonkeyDevice(MonkeyDevice device) {
+ this.mDevice = device;
+ this.mHierarchyViewer = device.getImpl().getHierarchyViewer();
+ }
+
+ @MonkeyRunnerExported(doc = "Sends a touch event to the selected object.",
+ args = { "selector", "type" },
+ argDocs = {
+ "The selector identifying the object.",
+ "The event type as returned by TouchPressType()." })
+ public void touch(PyObject[] args, String[] kws) {
+ ArgParser ap = JythonUtils.createArgParser(args, kws);
+ Preconditions.checkNotNull(ap);
+
+ By selector = getSelector(ap, 0);
+ String tmpType = ap.getString(1);
+ TouchPressType type = MonkeyDevice.TOUCH_NAME_TO_ENUM.get(tmpType);
+ if (type == null) type = TouchPressType.DOWN_AND_UP;
+ // TODO: try catch rethrow PyExc
+ touch(selector, type);
+ }
+
+ public void touch(By selector, TouchPressType type) {
+ Point p = getElementCenter(selector);
+ mDevice.getImpl().touch(p.x, p.y, type);
+ }
+
+ @MonkeyRunnerExported(doc = "Types a string into the specified object.",
+ args = { "selector", "text" },
+ argDocs = {
+ "The selector identifying the object.",
+ "The text to type into the object." })
+ public void type(PyObject[] args, String[] kws) {
+ ArgParser ap = JythonUtils.createArgParser(args, kws);
+ Preconditions.checkNotNull(ap);
+
+ By selector = getSelector(ap, 0);
+ String text = ap.getString(1);
+ type(selector, text);
+ }
+
+ public void type(By selector, String text) {
+ Point p = getElementCenter(selector);
+ mDevice.getImpl().touch(p.x, p.y, TouchPressType.DOWN_AND_UP);
+ mDevice.getImpl().type(text);
+ }
+
+ @MonkeyRunnerExported(doc = "Locates the coordinates of the selected object.",
+ args = { "selector" },
+ argDocs = { "The selector identifying the object." },
+ returns = "Tuple containing (x,y,w,h) location and size.")
+ public PyTuple locate(PyObject[] args, String[] kws) {
+ ArgParser ap = JythonUtils.createArgParser(args, kws);
+ Preconditions.checkNotNull(ap);
+
+ By selector = getSelector(ap, 0);
+
+ ViewNode node = mHierarchyViewer.findView(selector);
+ Point p = HierarchyViewer.getAbsolutePositionOfView(node);
+ PyTuple tuple = new PyTuple(
+ new PyInteger(p.x),
+ new PyInteger(p.y),
+ new PyInteger(node.width),
+ new PyInteger(node.height));
+ return tuple;
+ }
+
+ @MonkeyRunnerExported(doc = "Checks if the specified object exists.",
+ args = { "selector" },
+ returns = "True if the object exists.",
+ argDocs = { "The selector identifying the object." })
+ public boolean exists(PyObject[] args, String[] kws) {
+ ArgParser ap = JythonUtils.createArgParser(args, kws);
+ Preconditions.checkNotNull(ap);
+
+ By selector = getSelector(ap, 0);
+ return exists(selector);
+ }
+
+ public boolean exists(By selector) {
+ ViewNode node = mHierarchyViewer.findView(selector);
+ return node != null;
+ }
+
+ @MonkeyRunnerExported(doc = "Checks if the specified object is visible.",
+ args = { "selector" },
+ returns = "True if the object is visible.",
+ argDocs = { "The selector identifying the object." })
+ public boolean visible(PyObject[] args, String[] kws) {
+ ArgParser ap = JythonUtils.createArgParser(args, kws);
+ Preconditions.checkNotNull(ap);
+
+ By selector = getSelector(ap, 0);
+ return visible(selector);
+ }
+
+ public boolean visible(By selector) {
+ return mHierarchyViewer.visible(selector);
+ }
+
+ @MonkeyRunnerExported(doc = "Obtain the text in the selected input box.",
+ args = { "selector" },
+ argDocs = { "The selector identifying the object." },
+ returns = "Text in the selected input box.")
+ public String getText(PyObject[] args, String[] kws) {
+ ArgParser ap = JythonUtils.createArgParser(args, kws);
+ Preconditions.checkNotNull(ap);
+
+ By selector = getSelector(ap, 0);
+ return getText(selector);
+ }
+
+ public String getText(By selector) {
+ return mHierarchyViewer.getText(selector);
+ }
+
+ @MonkeyRunnerExported(doc = "Gets the id of the focused window.",
+ returns = "The symbolic id of the focused window or None.")
+ public String getFocusedWindowId(PyObject[] args, String[] kws) {
+ return getFocusedWindowId();
+ }
+
+ public String getFocusedWindowId() {
+ return mHierarchyViewer.getFocusedWindowName();
+ }
+
+ /**
+ * Forwards unknown methods to the original MonkeyDevice object.
+ */
+ @Override
+ public PyObject __findattr_ex__(String name) {
+ if (!EXPORTED_METHODS.contains(name)) {
+ return mDevice.__findattr_ex__(name);
+ }
+ return super.__findattr_ex__(name);
+ }
+
+ /**
+ * Get the selector object from the argument parser.
+ *
+ * @param ap argument parser to get it from.
+ * @param i argument index.
+ * @return selector object.
+ */
+ private By getSelector(ArgParser ap, int i) {
+ return (By)ap.getPyObject(i).__tojava__(By.class);
+ }
+
+ /**
+ * Get the coordinates of the element's center.
+ *
+ * @param selector the element selector
+ * @return the (x,y) coordinates of the center
+ */
+ private Point getElementCenter(By selector) {
+ ViewNode node = mHierarchyViewer.findView(selector);
+ if (node == null) {
+ throw new PyException(Py.ValueError,
+ String.format("View not found: %s", selector));
+ }
+
+ Point p = HierarchyViewer.getAbsoluteCenterOfView(node);
+ return p;
+ }
+
+} \ No newline at end of file
diff --git a/monkeyrunner/src/com/android/monkeyrunner/easy/HierarchyViewer.java b/monkeyrunner/src/com/android/monkeyrunner/easy/HierarchyViewer.java
new file mode 100644
index 0000000..450571c
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/easy/HierarchyViewer.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2011 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.easy;
+
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.Log;
+import com.android.hierarchyviewerlib.device.DeviceBridge;
+import com.android.hierarchyviewerlib.device.ViewNode;
+import com.android.hierarchyviewerlib.device.Window;
+
+import org.eclipse.swt.graphics.Point;
+
+/**
+ * Class for querying the view hierarchy of the device.
+ */
+public class HierarchyViewer {
+ public static final String TAG = "hierarchyviewer";
+
+ private IDevice mDevice;
+
+ /**
+ * Constructs the hierarchy viewer for the specified device.
+ *
+ * @param device The Android device to connect to.
+ */
+ public HierarchyViewer(IDevice device) {
+ this.mDevice = device;
+ setupViewServer();
+ }
+
+ private void setupViewServer() {
+ DeviceBridge.setupDeviceForward(mDevice);
+ if (!DeviceBridge.isViewServerRunning(mDevice)) {
+ if (!DeviceBridge.startViewServer(mDevice)) {
+ // TODO: Get rid of this delay.
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ }
+ if (!DeviceBridge.startViewServer(mDevice)) {
+ Log.e(TAG, "Unable to debug device " + mDevice);
+ throw new RuntimeException("Could not connect to the view server");
+ }
+ return;
+ }
+ }
+ DeviceBridge.loadViewServerInfo(mDevice);
+ }
+
+ /**
+ * Finds a view using a selector. Currently only supports selectors which
+ * specify an id.
+ *
+ * @param selector selector for the view.
+ * @return view with the specified ID, or {@code null} if no view found.
+ */
+ public ViewNode findView(By selector) {
+ ViewNode rootNode = DeviceBridge.loadWindowData(
+ new Window(mDevice, "", 0xffffffff));
+ if (rootNode == null) {
+ throw new RuntimeException("Could not dump view");
+ }
+ return selector.find(rootNode);
+ }
+
+ /**
+ * Gets the window that currently receives the focus.
+ *
+ * @return name of the window that currently receives the focus.
+ */
+ public String getFocusedWindowName() {
+ int id = DeviceBridge.getFocusedWindow(mDevice);
+ Window[] windows = DeviceBridge.loadWindows(mDevice);
+ for (Window w : windows) {
+ if (w.getHashCode() == id)
+ return w.getTitle();
+ }
+ return null;
+ }
+
+ /**
+ * Gets the absolute x/y position of the view node.
+ *
+ * @param node view node to find position of.
+ * @return point specifying the x/y position of the node.
+ */
+ public static Point getAbsolutePositionOfView(ViewNode node) {
+ int x = node.left;
+ int y = node.top;
+ ViewNode p = node.parent;
+ while (p != null) {
+ x += p.left - p.scrollX;
+ y += p.top - p.scrollY;
+ p = p.parent;
+ }
+ return new Point(x, y);
+ }
+
+ /**
+ * Gets the absolute x/y center of the specified view node.
+ *
+ * @param node view node to find position of.
+ * @return absolute x/y center of the specified view node.
+ */
+ public static Point getAbsoluteCenterOfView(ViewNode node) {
+ Point point = getAbsolutePositionOfView(node);
+ return new Point(
+ point.x + (node.width / 2), point.y + (node.height / 2));
+ }
+
+ /**
+ * Gets the visibility of a given element.
+ *
+ * @param selector selector for the view.
+ * @return True if the element is visible.
+ */
+ public boolean visible(By selector) {
+ ViewNode node = findView(selector);
+ boolean ret = (node != null)
+ && node.namedProperties.containsKey("getVisibility()")
+ && "VISIBLE".equalsIgnoreCase(
+ node.namedProperties.get("getVisibility()").value);
+ return ret;
+
+ }
+
+ /**
+ * Gets the text of a given element.
+ *
+ * @param selector selector for the view.
+ * @return the text of the given element.
+ */
+ public String getText(By selector) {
+ ViewNode node = findView(selector);
+ if (node == null) {
+ throw new RuntimeException("Node not found");
+ }
+ ViewNode.Property textProperty = node.namedProperties.get("text:mText");
+ if (textProperty == null) {
+ throw new RuntimeException("No text property on node");
+ }
+ return textProperty.value;
+ }
+}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/easy/README b/monkeyrunner/src/com/android/monkeyrunner/easy/README
new file mode 100644
index 0000000..239bedd
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/easy/README
@@ -0,0 +1,27 @@
+com.android.monkeyrunner.easy contains classes intended to make it easier
+to interact with applications using the MonkeyRunner framework. Instead of
+referencing a button or input box by x,y coordinate, they can be referenced
+by identifier, as in the following Python example:
+
+##############################################################################
+
+from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
+from com.android.monkeyrunner.easy import EasyMonkeyDevice
+from com.android.monkeyrunner.easy import By
+
+# Connect to the current device.
+device = MonkeyRunner.waitForConnection()
+
+# Use the EasyMonkey API, all methods on device are available in easy_device.
+easy_device = EasyMonkeyDevice(device)
+
+if not easy_device.visible(By.id('id/all_apps_button')):
+ raise Error('Could not find the "all apps" button')
+
+print "Location of element:", easy_device.locate(By.id('id/all_apps_button'))
+
+easy_device.touch(By.id('id/all_apps_button'), 'DOWN_AND_UP')
+
+##############################################################################
+
+WARNING: This API is under development and may change without notice.
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java
index c1a8f7f..914a5b9 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java
@@ -15,8 +15,9 @@
*/
package com.android.monkeyrunner.recorder;
-import com.android.monkeyrunner.MonkeyDevice;
import com.android.monkeyrunner.adb.AdbBackend;
+import com.android.monkeyrunner.core.IMonkeyBackend;
+import com.android.monkeyrunner.core.IMonkeyDevice;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
@@ -44,7 +45,7 @@ public class MonkeyRecorder {
*
* @param device
*/
- public static void start(final MonkeyDevice device) {
+ public static void start(final IMonkeyDevice device) {
MonkeyRecorderFrame frame = new MonkeyRecorderFrame(device);
// TODO: this is a hack until the window listener works.
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
@@ -69,7 +70,7 @@ public class MonkeyRecorder {
}
public static void main(String[] args) {
- AdbBackend adb = new AdbBackend();
+ IMonkeyBackend adb = new AdbBackend();
MonkeyRecorder.start(adb.waitForConnection());
}
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java
index b6c1f78..88c1e16 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java
@@ -16,7 +16,8 @@
package com.android.monkeyrunner.recorder;
import com.android.monkeyrunner.MonkeyDevice;
-import com.android.monkeyrunner.MonkeyImage;
+import com.android.monkeyrunner.core.IMonkeyImage;
+import com.android.monkeyrunner.core.IMonkeyDevice;
import com.android.monkeyrunner.recorder.actions.Action;
import com.android.monkeyrunner.recorder.actions.DragAction;
import com.android.monkeyrunner.recorder.actions.DragAction.Direction;
@@ -60,7 +61,7 @@ public class MonkeyRecorderFrame extends JFrame {
private static final Logger LOG =
Logger.getLogger(MonkeyRecorderFrame.class.getName());
- private final MonkeyDevice device;
+ private final IMonkeyDevice device;
private static final long serialVersionUID = 1L;
private JPanel jContentPane = null;
@@ -83,6 +84,7 @@ public class MonkeyRecorderFrame extends JFrame {
private ActionListModel actionListModel;
private final Timer refreshTimer = new Timer(1000, new ActionListener() {
+ @Override
public void actionPerformed(ActionEvent e) {
refreshDisplay(); // @jve:decl-index=0:
}
@@ -91,7 +93,7 @@ public class MonkeyRecorderFrame extends JFrame {
/**
* This is the default constructor
*/
- public MonkeyRecorderFrame(MonkeyDevice device) {
+ public MonkeyRecorderFrame(IMonkeyDevice device) {
this.device = device;
initialize();
}
@@ -110,7 +112,7 @@ public class MonkeyRecorderFrame extends JFrame {
}
private void refreshDisplay() {
- MonkeyImage snapshot = device.takeSnapshot();
+ IMonkeyImage snapshot = device.takeSnapshot();
currentImage = snapshot.createBufferedImage();
Graphics2D g = scaledImage.createGraphics();
@@ -200,6 +202,7 @@ public class MonkeyRecorderFrame extends JFrame {
waitButton = new JButton();
waitButton.setText("Wait");
waitButton.addActionListener(new java.awt.event.ActionListener() {
+ @Override
public void actionPerformed(java.awt.event.ActionEvent e) {
String howLongStr = JOptionPane.showInputDialog("How many seconds to wait?");
if (howLongStr != null) {
@@ -222,6 +225,7 @@ public class MonkeyRecorderFrame extends JFrame {
pressButton = new JButton();
pressButton.setText("Press a Button");
pressButton.addActionListener(new java.awt.event.ActionListener() {
+ @Override
public void actionPerformed(java.awt.event.ActionEvent e) {
JPanel panel = new JPanel();
JLabel text = new JLabel("What button to press?");
@@ -255,6 +259,7 @@ public class MonkeyRecorderFrame extends JFrame {
typeButton = new JButton();
typeButton.setText("Type Something");
typeButton.addActionListener(new java.awt.event.ActionListener() {
+ @Override
public void actionPerformed(java.awt.event.ActionEvent e) {
String whatToType = JOptionPane.showInputDialog("What to type?");
if (whatToType != null) {
@@ -276,6 +281,7 @@ public class MonkeyRecorderFrame extends JFrame {
flingButton = new JButton();
flingButton.setText("Fling");
flingButton.addActionListener(new java.awt.event.ActionListener() {
+ @Override
public void actionPerformed(java.awt.event.ActionEvent e) {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
@@ -358,6 +364,7 @@ public class MonkeyRecorderFrame extends JFrame {
exportActionButton = new JButton();
exportActionButton.setText("Export Actions");
exportActionButton.addActionListener(new java.awt.event.ActionListener() {
+ @Override
public void actionPerformed(java.awt.event.ActionEvent ev) {
JFileChooser fc = new JFileChooser();
if (fc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
@@ -383,6 +390,7 @@ public class MonkeyRecorderFrame extends JFrame {
refreshButton = new JButton();
refreshButton.setText("Refresh Display");
refreshButton.addActionListener(new java.awt.event.ActionListener() {
+ @Override
public void actionPerformed(java.awt.event.ActionEvent e) {
refreshDisplay();
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java
index d582aa4..6fa91ab 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java
@@ -15,7 +15,7 @@
*/
package com.android.monkeyrunner.recorder.actions;
-import com.android.monkeyrunner.MonkeyDevice;
+import com.android.monkeyrunner.core.IMonkeyDevice;
/**
* All actions that can be recorded must implement this interface.
@@ -41,5 +41,5 @@ public interface Action {
*
* @param device the device to execute the action on.
*/
- void execute(MonkeyDevice device) throws Exception;
+ void execute(IMonkeyDevice device) throws Exception;
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java
index 082bfe4..2461c0d 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java
@@ -15,7 +15,7 @@
*/
package com.android.monkeyrunner.recorder.actions;
-import com.android.monkeyrunner.MonkeyDevice;
+import com.android.monkeyrunner.core.IMonkeyDevice;
/**
* Action to drag the "finger" across the device.
@@ -77,7 +77,7 @@ public class DragAction implements Action {
}
@Override
- public void execute(MonkeyDevice device) {
+ public void execute(IMonkeyDevice device) {
device.drag(startx, starty, endx, endy, steps, timeMs);
}
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java
index a0d9e0e..66a933a 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java
@@ -19,6 +19,7 @@ import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.android.monkeyrunner.MonkeyDevice;
+import com.android.monkeyrunner.core.IMonkeyDevice;
/**
* Action to press a certain button.
@@ -60,7 +61,7 @@ public class PressAction implements Action {
}
@Override
- public void execute(MonkeyDevice device) {
+ public void execute(IMonkeyDevice device) {
device.press(key,
MonkeyDevice.TOUCH_NAME_TO_ENUM.get(downUpFlag));
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java
index 4633edb..4e0ae2d 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java
@@ -19,6 +19,7 @@ import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.android.monkeyrunner.MonkeyDevice;
+import com.android.monkeyrunner.core.IMonkeyDevice;
/**
* Action to touch the touchscreen at a certain location.
@@ -46,7 +47,7 @@ public class TouchAction implements Action {
}
@Override
- public void execute(MonkeyDevice device) throws Exception {
+ public void execute(IMonkeyDevice device) throws Exception {
device.touch(x, y,
MonkeyDevice.TOUCH_NAME_TO_ENUM.get(direction));
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java
index 1bfb9e9..78e90b0 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java
@@ -15,7 +15,7 @@
*/
package com.android.monkeyrunner.recorder.actions;
-import com.android.monkeyrunner.MonkeyDevice;
+import com.android.monkeyrunner.core.IMonkeyDevice;
/**
* Action to type in a string on the device.
@@ -34,12 +34,13 @@ public class TypeAction implements Action {
@Override
public String serialize() {
- String pydict = PyDictUtilBuilder.newBuilder().add("message", whatToType).build();
+ String pydict = PyDictUtilBuilder.newBuilder()
+ .add("message", whatToType).build();
return "TYPE|" + pydict;
}
@Override
- public void execute(MonkeyDevice device) {
+ public void execute(IMonkeyDevice device) {
device.type(whatToType);
}
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java
index 9115f9a..bd2d421 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java
@@ -15,7 +15,7 @@
*/
package com.android.monkeyrunner.recorder.actions;
-import com.android.monkeyrunner.MonkeyDevice;
+import com.android.monkeyrunner.core.IMonkeyDevice;
/**
* Action that specifies to wait for a certain amount of time.
@@ -27,19 +27,16 @@ public class WaitAction implements Action {
this.howLongSeconds = howLongSeconds;
}
- @Override
public String getDisplayName() {
return String.format("Wait for %g seconds", this.howLongSeconds);
}
- @Override
public String serialize() {
String pydict = PyDictUtilBuilder.newBuilder().add("seconds", howLongSeconds).build();
return "WAIT|" + pydict;
}
- @Override
- public void execute(MonkeyDevice device) throws Exception {
+ public void execute(IMonkeyDevice device) throws Exception {
long ms = (long) (1000.0f * howLongSeconds);
Thread.sleep(ms);
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/stub/StubBackend.java b/monkeyrunner/src/com/android/monkeyrunner/stub/StubBackend.java
index c2fa5f7..b868bf1 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/stub/StubBackend.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/stub/StubBackend.java
@@ -15,18 +15,22 @@
*/
package com.android.monkeyrunner.stub;
-import com.android.monkeyrunner.MonkeyDevice;
import com.android.monkeyrunner.MonkeyManager;
-import com.android.monkeyrunner.MonkeyRunnerBackend;
-
-public class StubBackend implements MonkeyRunnerBackend {
+import com.android.monkeyrunner.core.IMonkeyBackend;
+import com.android.monkeyrunner.core.IMonkeyDevice;
+public class StubBackend implements IMonkeyBackend {
public MonkeyManager createManager(String address, int port) {
// TODO Auto-generated method stub
return null;
}
- public MonkeyDevice waitForConnection(long timeout, String deviceId) {
+ public IMonkeyDevice waitForConnection() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public IMonkeyDevice waitForConnection(long timeout, String deviceId) {
// TODO Auto-generated method stub
return null;
}
diff --git a/monkeyrunner/test/com/android/monkeyrunner/JythonUtilsTest.java b/monkeyrunner/test/com/android/monkeyrunner/JythonUtilsTest.java
index 5b8c8f9..5f781f1 100644
--- a/monkeyrunner/test/com/android/monkeyrunner/JythonUtilsTest.java
+++ b/monkeyrunner/test/com/android/monkeyrunner/JythonUtilsTest.java
@@ -23,6 +23,7 @@ import com.android.monkeyrunner.doc.MonkeyRunnerExported;
import junit.framework.TestCase;
import org.python.core.ArgParser;
+import org.python.core.ClassDictInit;
import org.python.core.PyDictionary;
import org.python.core.PyException;
import org.python.core.PyObject;
@@ -30,6 +31,7 @@ import org.python.core.PyString;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Unit tests for the JythonUtils class.
@@ -37,6 +39,7 @@ import java.util.Map;
public class JythonUtilsTest extends TestCase {
private static final String PACKAGE_NAME = JythonUtilsTest.class.getPackage().getName();
private static final String CLASS_NAME = JythonUtilsTest.class.getSimpleName();
+ private static final String EXECUTABLE_PATH = "string";
private static boolean called = false;
private static double floatValue = 0.0;
@@ -102,7 +105,7 @@ public class JythonUtilsTest extends TestCase {
}
sb.append(")");
- return ScriptRunner.runStringAndGet(sb.toString(), "result").get("result");
+ return ScriptRunner.runStringAndGet(EXECUTABLE_PATH, sb.toString(), "result").get("result");
}
public void testSimpleCall() {
@@ -221,4 +224,34 @@ public class JythonUtilsTest extends TestCase {
double d = (Double) doublePyObject.__tojava__(Double.class);
assertEquals(3.14, d);
}
+
+ /**
+ * Base class to test overridden methods.
+ */
+ static class PythonMethodsClass extends PyObject implements ClassDictInit {
+ public static void classDictInit(PyObject dict) {
+ JythonUtils.convertDocAnnotationsForClass(PythonMethodsClass.class, dict);
+ }
+
+ @MonkeyRunnerExported(doc = "The first method.")
+ public void firstMethod(PyObject[] args, String[] kws) {
+ }
+
+ @MonkeyRunnerExported(doc = "The second method.")
+ public void secondMethod(PyObject[] args, String[] kws) {
+ }
+
+ public void unattributedMethod() {
+ }
+ }
+
+ public void testGetPythonMethods() {
+ Set<String> methods = JythonUtils.getMethodNames(PythonMethodsClass.class);
+ assertEquals(2, methods.size());
+ assertTrue(methods.contains("firstMethod"));
+ assertTrue(methods.contains("secondMethod"));
+
+ // Make sure it works on non-Jython objects.
+ assertTrue(JythonUtils.getMethodNames(String.class).isEmpty());
+ }
}
diff --git a/ninepatch/Android.mk b/ninepatch/Android.mk
index 42e0205..5f6cbed 100644
--- a/ninepatch/Android.mk
+++ b/ninepatch/Android.mk
@@ -21,3 +21,6 @@ LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_MODULE := ninepatch
include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/ninepatch/NOTICE b/ninepatch/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/ninepatch/NOTICE
@@ -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/ninepatch/tests/.classpath b/ninepatch/tests/.classpath
new file mode 100644
index 0000000..26542d3
--- /dev/null
+++ b/ninepatch/tests/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/ninepatch"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/ninepatch/tests/.project b/ninepatch/tests/.project
new file mode 100644
index 0000000..3d049cf
--- /dev/null
+++ b/ninepatch/tests/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>ninepatch-tests</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/hierarchyviewer2/app/src/Android.mk b/ninepatch/tests/Android.mk
index 1f15bee..8a9fd71 100644
--- a/hierarchyviewer2/app/src/Android.mk
+++ b/ninepatch/tests/Android.mk
@@ -13,21 +13,21 @@
# limitations under the License.
LOCAL_PATH := $(call my-dir)
+
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+# Only compile source java files in this lib.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := res
-LOCAL_JAR_MANIFEST := ../etc/manifest.txt
-LOCAL_JAVA_LIBRARIES := \
- ddmlib \
- ddmuilib \
- hierarchyviewerlib \
- swt \
- org.eclipse.jface_3.4.2.M20090107-0800 \
- org.eclipse.core.commands_3.4.0.I20080509-2000 \
- sdklib
+LOCAL_MODULE := ninepatch-tests
+LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := hierarchyviewer2
+LOCAL_JAVA_LIBRARIES := junit
+# bundle ninepatch inside the test jar for continuous tests
+LOCAL_STATIC_JAVA_LIBRARIES := ninepatch
include $(BUILD_HOST_JAVA_LIBRARY)
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/ninepatch/tests/res/com/android/ninepatch/button.9.png b/ninepatch/tests/res/com/android/ninepatch/button.9.png
new file mode 100644
index 0000000..9d52f40
--- /dev/null
+++ b/ninepatch/tests/res/com/android/ninepatch/button.9.png
Binary files differ
diff --git a/ninepatch/tests/src/com/android/ninepatch/NinePatchTest.java b/ninepatch/tests/src/com/android/ninepatch/NinePatchTest.java
new file mode 100644
index 0000000..1722b55
--- /dev/null
+++ b/ninepatch/tests/src/com/android/ninepatch/NinePatchTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.android.ninepatch;
+
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+
+public class NinePatchTest extends TestCase {
+
+ private NinePatch mPatch;
+
+ @Override
+ protected void setUp() throws Exception {
+ InputStream stream = this.getClass().getResourceAsStream("button.9.png");
+
+ mPatch = NinePatch.load(stream, true /* is9Patch*/, false /* convert */);
+ }
+
+ public void test9PatchLoad() throws Exception {
+ assertNotNull(mPatch);
+ }
+
+ public void test9PatchMinSize() {
+ int[] padding = new int[4];
+ mPatch.getPadding(padding);
+ assertEquals(13, padding[0]);
+ assertEquals(3, padding[1]);
+ assertEquals(13, padding[2]);
+ assertEquals(4, padding[3]);
+ assertEquals(36, mPatch.getWidth());
+ assertEquals(25, mPatch.getHeight());
+ }
+}
diff --git a/screenshot/NOTICE b/screenshot/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/screenshot/NOTICE
@@ -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/sdklauncher/NOTICE b/sdklauncher/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/sdklauncher/NOTICE
@@ -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/sdkmanager/app/.classpath b/sdkmanager/app/.classpath
index 3380ba1..50576bb 100644
--- a/sdkmanager/app/.classpath
+++ b/sdkmanager/app/.classpath
@@ -1,12 +1,14 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="tests"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
- <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
- <classpathentry combineaccessrules="false" kind="src" path="/SdkUiLib"/>
- <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
- <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry excluding="**/Android.mk" kind="src" path="src"/>
+ <classpathentry excluding="**/Android.mk" kind="src" path="tests"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/SdkUiLib"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/common"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
+
diff --git a/sdkmanager/app/.settings/org.eclipse.jdt.core.prefs b/sdkmanager/app/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a363b10
--- /dev/null
+++ b/sdkmanager/app/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 16 15:11:09 PDT 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/sdkmanager/app/Android.mk b/sdkmanager/app/Android.mk
index 24ba61f..d7b630e 100644
--- a/sdkmanager/app/Android.mk
+++ b/sdkmanager/app/Android.mk
@@ -1,5 +1,33 @@
# Copyright 2007 The Android Open Source Project
#
-SDKMANAGERAPP_LOCAL_DIR := $(call my-dir)
-include $(SDKMANAGERAPP_LOCAL_DIR)/etc/Android.mk
-include $(SDKMANAGERAPP_LOCAL_DIR)/src/Android.mk
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_JAR_MANIFEST := etc/manifest.txt
+
+# IMPORTANT: if you add a new dependency here, please make sure
+# to also check the following files:
+# sdkmanager/app/etc/manifest.txt
+# sdkmanager/app/etc/android.bat
+# (Note that we don't reference swt.jar in these files since
+# it is dynamically added by android.bat/.sh based on whether the
+# current VM is 32 or 64 bit.)
+LOCAL_JAVA_LIBRARIES := \
+ androidprefs \
+ sdklib \
+ sdkuilib \
+ swt \
+ org.eclipse.jface_3.4.2.M20090107-0800 \
+ org.eclipse.equinox.common_3.4.0.v20080421-2006 \
+ org.eclipse.core.commands_3.4.0.I20080509-2000
+
+LOCAL_MODULE := sdkmanager
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/sdkmanager/app/NOTICE b/sdkmanager/app/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/sdkmanager/app/NOTICE
@@ -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/sdkmanager/app/etc/android b/sdkmanager/app/etc/android
index 2e17009..555e071 100755
--- a/sdkmanager/app/etc/android
+++ b/sdkmanager/app/etc/android
@@ -74,9 +74,10 @@ fi
if [ "$OSTYPE" = "cygwin" ] ; then
jarpath=`cygpath -w "$frameworkdir/$jarfile"`
+ jarpath="$jarpath;"`cygpath -w "$frameworkdir/swtmenubar.jar"`
progdir=`cygpath -w "$progdir"`
else
- jarpath="$frameworkdir/$jarfile"
+ jarpath="$frameworkdir/$jarfile:$frameworkdir/swtmenubar.jar"
fi
# Get the current content of java.ext.dirs so that we can add to it instead of replacing
@@ -105,9 +106,9 @@ if [ ! -d "$swtpath" ]; then
exit 1
fi
-if [ -z "$1" ]; then
- echo "Starting Android SDK and AVD Manager"
-fi
-
# need to use "java.ext.dirs" because "-jar" causes classpath to be ignored
-exec "$java_cmd" -Xmx256M $os_opts $java_debug -Dcom.android.sdkmanager.toolsdir="$progdir" -classpath "$jarpath:$swtpath/swt.jar" com.android.sdkmanager.Main "$@"
+exec "$java_cmd" \
+ -Xmx256M $os_opts $java_debug \
+ -Dcom.android.sdkmanager.toolsdir="$progdir" \
+ -classpath "$jarpath:$swtpath/swt.jar" \
+ com.android.sdkmanager.Main "$@"
diff --git a/sdkmanager/app/etc/android.bat b/sdkmanager/app/etc/android.bat
index daa6b8a..0d83734 100755
--- a/sdkmanager/app/etc/android.bat
+++ b/sdkmanager/app/etc/android.bat
@@ -37,7 +37,7 @@ set java_exe=
call lib\find_java.bat
if not defined java_exe goto :EOF
-set jar_path=lib\sdkmanager.jar
+set jar_path=lib\sdkmanager.jar;lib\swtmenubar.jar
rem Set SWT.Jar path based on current architecture (x86 or x86_64)
for /f %%a in ('%java_exe% -jar lib\archquery.jar') do set swt_path=lib\%%a
@@ -45,7 +45,7 @@ for /f %%a in ('%java_exe% -jar lib\archquery.jar') do set swt_path=lib\%%a
if "%1 %2"=="update sdk" goto StartUi
if not "%1"=="" goto EndTempCopy
:StartUi
- echo [INFO] Starting Android SDK and AVD Manager
+ rem Starting Android SDK and AVD Manager UI
rem We're now going to create a temp dir to hold all the Jar files needed
rem to run the android tool, copy them in the temp dir and finally execute
diff --git a/sdkmanager/app/etc/manifest.txt b/sdkmanager/app/etc/manifest.txt
index 51845c7..bb2e8c4 100644
--- a/sdkmanager/app/etc/manifest.txt
+++ b/sdkmanager/app/etc/manifest.txt
@@ -1,2 +1,2 @@
Main-Class: com.android.sdkmanager.Main
-Class-Path: androidprefs.jar common.jar sdklib.jar sdkuilib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar
+Class-Path: androidprefs.jar common.jar sdklib.jar sdkuilib.jar swtmenubar.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar
diff --git a/sdkmanager/app/src/Android.mk b/sdkmanager/app/src/Android.mk
deleted file mode 100644
index 7520f96..0000000
--- a/sdkmanager/app/src/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright 2007 The Android Open Source Project
-#
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_JAVA_RESOURCE_DIRS := .
-
-LOCAL_JAR_MANIFEST := ../etc/manifest.txt
-
-# If the dependency list is changed, etc/manifest.txt
-# MUST be updated as well (Except for swt.jar which is dynamically
-# added based on whether the VM is 32 or 64 bit)
-LOCAL_JAVA_LIBRARIES := \
- androidprefs \
- sdklib \
- sdkuilib \
- swt \
- org.eclipse.jface_3.4.2.M20090107-0800 \
- org.eclipse.equinox.common_3.4.0.v20080421-2006 \
- org.eclipse.core.commands_3.4.0.I20080509-2000
-
-LOCAL_MODULE := sdkmanager
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
diff --git a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
index 8f5dec4..50fc496 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
@@ -47,10 +47,10 @@ class CommandLineProcessor {
*/
/** Internal verb name for internally hidden flags. */
- public final static String GLOBAL_FLAG_VERB = "@@internal@@";
+ public final static String GLOBAL_FLAG_VERB = "@@internal@@"; //$NON-NLS-1$
/** String to use when the verb doesn't need any object. */
- public final static String NO_VERB_OBJECT = "";
+ public final static String NO_VERB_OBJECT = ""; //$NON-NLS-1$
/** The global help flag. */
public static final String KEY_HELP = "help";
@@ -183,7 +183,7 @@ class CommandLineProcessor {
public Object getValue(String verb, String directObject, String longFlagName) {
if (verb != null && directObject != null) {
- String key = verb + "/" + directObject + "/" + longFlagName;
+ String key = verb + '/' + directObject + '/' + longFlagName;
Arg arg = mArguments.get(key);
return arg.getCurrentValue();
}
@@ -216,7 +216,7 @@ class CommandLineProcessor {
* argument mode.
*/
protected void setValue(String verb, String directObject, String longFlagName, Object value) {
- String key = verb + "/" + directObject + "/" + longFlagName;
+ String key = verb + '/' + directObject + '/' + longFlagName;
Arg arg = mArguments.get(key);
arg.setCurrentValue(value);
}
@@ -238,16 +238,16 @@ class CommandLineProcessor {
for (int i = 0; i < n; i++) {
Arg arg = null;
String a = args[i];
- if (a.startsWith("--")) {
+ if (a.startsWith("--")) { //$NON-NLS-1$
arg = findLongArg(verb, directObject, a.substring(2));
- } else if (a.startsWith("-")) {
+ } else if (a.startsWith("-")) { //$NON-NLS-1$
arg = findShortArg(verb, directObject, a.substring(1));
}
// No matching argument name found
if (arg == null) {
// Does it looks like a dashed parameter?
- if (a.startsWith("-")) {
+ if (a.startsWith("-")) { //$NON-NLS-1$
if (verb == null || directObject == null) {
// It looks like a dashed parameter and we don't have a a verb/object
// set yet, the parameter was just given too early.
@@ -330,9 +330,9 @@ class CommandLineProcessor {
String b = args[i];
Arg dummyArg = null;
- if (b.startsWith("--")) {
+ if (b.startsWith("--")) { //$NON-NLS-1$
dummyArg = findLongArg(verb, directObject, b.substring(2));
- } else if (b.startsWith("-")) {
+ } else if (b.startsWith("-")) { //$NON-NLS-1$
dummyArg = findShortArg(verb, directObject, b.substring(1));
}
if (dummyArg != null) {
@@ -352,7 +352,7 @@ class CommandLineProcessor {
// used to print specific help.
// Setting a non-null error message triggers printing the help, however
// there is no specific error to print.
- errorMsg = "";
+ errorMsg = ""; //$NON-NLS-1$
}
}
@@ -392,9 +392,9 @@ class CommandLineProcessor {
arg.getDirectObject().equals(directObject)) {
if (arg.isMandatory() && arg.getCurrentValue() == null) {
if (missing == null) {
- missing = "--" + arg.getLongArg();
+ missing = "--" + arg.getLongArg(); //$NON-NLS-1$
} else {
- missing += ", --" + arg.getLongArg();
+ missing += ", --" + arg.getLongArg(); //$NON-NLS-1$
plural = true;
}
}
@@ -432,7 +432,7 @@ class CommandLineProcessor {
if (directObject == null) {
directObject = NO_VERB_OBJECT;
}
- String key = verb + "/" + directObject + "/" + longName;
+ String key = verb + '/' + directObject + '/' + longName; //$NON-NLS-1$
return mArguments.get(key);
}
@@ -497,7 +497,7 @@ class CommandLineProcessor {
"\n" +
"Global options:",
verb == null ? "action" :
- verb + (directObject == null ? "" : " " + directObject));
+ verb + (directObject == null ? "" : " " + directObject)); //$NON-NLS-1$
listOptions(GLOBAL_FLAG_VERB, NO_VERB_OBJECT);
if (verb == null || directObject == null) {
@@ -552,8 +552,8 @@ class CommandLineProcessor {
Arg arg = entry.getValue();
if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) {
- String value = "";
- String required = "";
+ String value = ""; //$NON-NLS-1$
+ String required = ""; //$NON-NLS-1$
if (arg.isMandatory()) {
required = " [required]";
@@ -828,7 +828,7 @@ class CommandLineProcessor {
* Internal helper to define a new argument for a give action.
*
* @param mode The {@link Mode} for the argument.
- * @param mandatory The argument is required (never if {@link Mode.BOOLEAN})
+ * @param mandatory The argument is required (never if {@link Mode#BOOLEAN})
* @param verb The verb name. Can be #INTERNAL_VERB.
* @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG.
* @param shortName The one-letter short argument name. Can be empty but not null.
@@ -853,7 +853,7 @@ class CommandLineProcessor {
directObject = NO_VERB_OBJECT;
}
- String key = verb + "/" + directObject + "/" + longName;
+ String key = verb + '/' + directObject + '/' + longName;
mArguments.put(key, new Arg(mode, mandatory,
verb, directObject, shortName, longName, description, defaultValue));
}
@@ -874,7 +874,7 @@ class CommandLineProcessor {
* @param args Format arguments.
*/
protected void stdout(String format, Object...args) {
- mLog.printf(format + "\n", args);
+ mLog.printf(format + '\n', args);
}
/**
diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 1fe6d97..5ac5f4c 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -16,30 +16,35 @@
package com.android.sdkmanager;
+import com.android.annotations.VisibleForTesting;
+import com.android.annotations.VisibleForTesting.Visibility;
+import com.android.io.FileWrapper;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
+import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdklib.internal.avd.HardwareProperties;
import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty;
import com.android.sdklib.internal.project.ProjectCreator;
-import com.android.sdklib.internal.project.ProjectCreator.OutputLevel;
import com.android.sdklib.internal.project.ProjectProperties;
+import com.android.sdklib.internal.project.ProjectCreator.OutputLevel;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
-import com.android.sdklib.io.FileWrapper;
+import com.android.sdklib.repository.SdkAddonConstants;
import com.android.sdklib.repository.SdkRepoConstants;
import com.android.sdklib.xml.AndroidXPathFactory;
import com.android.sdkmanager.internal.repository.AboutPage;
import com.android.sdkmanager.internal.repository.SettingsPage;
-import com.android.sdkuilib.internal.repository.LocalPackagesPage;
+import com.android.sdkuilib.internal.repository.PackagesPage;
import com.android.sdkuilib.internal.repository.UpdateNoWindow;
import com.android.sdkuilib.internal.widgets.MessageBoxLog;
+import com.android.sdkuilib.repository.IUpdaterWindow;
import com.android.sdkuilib.repository.UpdaterWindow;
+import com.android.util.Pair;
import org.eclipse.swt.widgets.Display;
import org.xml.sax.InputSource;
@@ -52,6 +57,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
@@ -220,6 +227,15 @@ public class Main {
} else if (SdkCommandLine.OBJECT_AVD.equals(directObject)) {
displayAvdList();
+ } else if (SdkCommandLine.OBJECT_SDK.equals(directObject)) {
+ // We don't support a specific GUI for this.
+ // If the user forces a gui mode to see this list, simply launch the regular GUI.
+ if (!mSdkCommandLine.getFlagNoUI(verb)) {
+ showMainWindow(false /*autoUpdate*/);
+ } else {
+ displayRemoteSdkListNoUI();
+ }
+
} else {
displayTargetList();
displayAvdList();
@@ -259,7 +275,7 @@ public class Main {
updateExportProject();
} else if (SdkCommandLine.OBJECT_SDK.equals(directObject)) {
- if (mSdkCommandLine.getFlagNoUI()) {
+ if (mSdkCommandLine.getFlagNoUI(verb)) {
updateSdkNoUI();
} else {
showMainWindow(true /*autoUpdate*/);
@@ -290,23 +306,19 @@ public class Main {
*/
private void showMainWindow(boolean autoUpdate) {
try {
- // display a message talking about the command line version
- System.out.printf("No command line parameters provided, launching UI.\n" +
- "See 'android --help' for operations from the command line.\n");
-
MessageBoxLog errorLogger = new MessageBoxLog(
"SDK Manager",
Display.getCurrent(),
true /*logErrorsOnly*/);
- UpdaterWindow window = new UpdaterWindow(
+ IUpdaterWindow window = new UpdaterWindow(
null /* parentShell */,
errorLogger,
mOsSdkFolder);
window.registerPage("Settings", SettingsPage.class);
window.registerPage("About", AboutPage.class);
if (autoUpdate) {
- window.setInitialPage(LocalPackagesPage.class);
+ window.setInitialPage(PackagesPage.class);
window.setRequestAutoUpdate(true);
}
window.open();
@@ -318,6 +330,18 @@ public class Main {
}
}
+ private void displayRemoteSdkListNoUI() {
+ boolean force = mSdkCommandLine.getFlagForce();
+ boolean useHttp = mSdkCommandLine.getFlagNoHttps();
+ boolean obsolete = mSdkCommandLine.getFlagObsolete();
+ String proxyHost = mSdkCommandLine.getParamProxyHost();
+ String proxyPort = mSdkCommandLine.getParamProxyPort();
+
+ UpdateNoWindow upd = new UpdateNoWindow(mOsSdkFolder, mSdkManager, mSdkLog,
+ force, useHttp, proxyHost, proxyPort);
+ upd.listRemotePackages(obsolete);
+ }
+
/**
* Updates the whole SDK without any UI, just using console output.
*/
@@ -326,40 +350,76 @@ public class Main {
boolean useHttp = mSdkCommandLine.getFlagNoHttps();
boolean dryMode = mSdkCommandLine.getFlagDryMode();
boolean obsolete = mSdkCommandLine.getFlagObsolete();
- String proxyHost = mSdkCommandLine.getProxyHost();
- String proxyPort = mSdkCommandLine.getProxyPort();
+ String proxyHost = mSdkCommandLine.getParamProxyHost();
+ String proxyPort = mSdkCommandLine.getParamProxyPort();
// Check filter types.
+ Pair<String, ArrayList<String>> filterResult =
+ checkFilterValues(mSdkCommandLine.getParamFilter());
+ if (filterResult.getFirst() != null) {
+ // We got an error.
+ errorAndExit(filterResult.getFirst());
+ }
+
+ UpdateNoWindow upd = new UpdateNoWindow(mOsSdkFolder, mSdkManager, mSdkLog,
+ force, useHttp, proxyHost, proxyPort);
+ upd.updateAll(filterResult.getSecond(), obsolete, dryMode);
+ }
+
+ /**
+ * Checks the values from the filter parameter and returns a tuple
+ * (error , accepted values). Either error is null and accepted values is not,
+ * or the reverse.
+ * <p/>
+ * Note that this is a quick sanity check of the --filter parameter *before* we
+ * start loading the remote repository sources. Loading the remotes takes a while
+ * so it's worth doing a quick sanity check before hand.
+ *
+ * @param filter A comma-separated list of keywords
+ * @return A pair <error string, usable values>, only one must be null and the other non-null.
+ */
+ @VisibleForTesting(visibility=Visibility.PRIVATE)
+ Pair<String, ArrayList<String>> checkFilterValues(String filter) {
ArrayList<String> pkgFilter = new ArrayList<String>();
- String filter = mSdkCommandLine.getParamFilter();
+
if (filter != null && filter.length() > 0) {
+ // Available types
+ Set<String> filterTypes = new TreeSet<String>();
+ filterTypes.addAll(Arrays.asList(SdkRepoConstants.NODES));
+ filterTypes.addAll(Arrays.asList(SdkAddonConstants.NODES));
+
for (String t : filter.split(",")) { //$NON-NLS-1$
- if (t != null) {
- t = t.trim();
- if (t.length() > 0) {
- boolean found = false;
- for (String t2 : SdkRepoConstants.NODES) {
- if (t2.equals(t)) {
- pkgFilter.add(t2);
- found = true;
- break;
- }
- }
- if (!found) {
- errorAndExit(
- "Unknown package filter type '%1$s'.\nAccepted values are: %2$s",
- t,
- Arrays.toString(SdkRepoConstants.NODES));
- return;
- }
- }
+ if (t == null) {
+ continue;
+ }
+ t = t.trim();
+ if (t.length() <= 0) {
+ continue;
+ }
+
+ if (t.replaceAll("[0-9]+", "").length() == 0) { //$NON-NLS-1$ //$NON-NLS-2$
+ // If the filter argument *only* contains digits, accept it.
+ // It's probably an index for the remote repository list,
+ // which we can't validate yet.
+ pkgFilter.add(t);
+ continue;
+ }
+
+ if (filterTypes.contains(t)) {
+ pkgFilter.add(t);
+ continue;
}
+
+ return Pair.of(
+ String.format(
+ "Unknown package filter type '%1$s'.\nAccepted values are: %2$s",
+ t,
+ Arrays.toString(filterTypes.toArray())),
+ null);
}
}
- UpdateNoWindow upd = new UpdateNoWindow(mOsSdkFolder, mSdkManager, mSdkLog,
- force, useHttp, proxyHost, proxyPort);
- upd.updateAll(pkgFilter, obsolete, dryMode);
+ return Pair.of(null, pkgFilter);
}
/**
@@ -720,6 +780,18 @@ public class Main {
* Displays the list of available Targets (Platforms and Add-ons)
*/
private void displayTargetList() {
+
+ // Compact output, suitable for scripts.
+ if (mSdkCommandLine != null && mSdkCommandLine.getFlagCompact()) {
+ char eol = mSdkCommandLine.getFlagEolNull() ? '\0' : '\n';
+
+ for (IAndroidTarget target : mSdkManager.getTargets()) {
+ mSdkLog.printf("%1$s%2$c", target.hashString(), eol);
+ }
+
+ return;
+ }
+
mSdkLog.printf("Available Android targets:\n");
int index = 1;
@@ -798,16 +870,30 @@ public class Main {
* @param avdManager
*/
public void displayAvdList(AvdManager avdManager) {
- mSdkLog.printf("Available Android Virtual Devices:\n");
AvdInfo[] avds = avdManager.getValidAvds();
+
+ // Compact output, suitable for scripts.
+ if (mSdkCommandLine != null && mSdkCommandLine.getFlagCompact()) {
+ char eol = mSdkCommandLine.getFlagEolNull() ? '\0' : '\n';
+
+ for (int index = 0 ; index < avds.length ; index++) {
+ AvdInfo info = avds[index];
+ mSdkLog.printf("%1$s%2$c", info.getName(), eol);
+ }
+
+ return;
+ }
+
+ mSdkLog.printf("Available Android Virtual Devices:\n");
+
for (int index = 0 ; index < avds.length ; index++) {
AvdInfo info = avds[index];
if (index > 0) {
mSdkLog.printf("---------\n");
}
mSdkLog.printf(" Name: %s\n", info.getName());
- mSdkLog.printf(" Path: %s\n", info.getPath());
+ mSdkLog.printf(" Path: %s\n", info.getDataFolderPath());
// get the target of the AVD
IAndroidTarget target = info.getTarget();
@@ -856,7 +942,8 @@ public class Main {
mSdkLog.printf("---------\n");
}
mSdkLog.printf(" Name: %s\n", info.getName() == null ? "--" : info.getName());
- mSdkLog.printf(" Path: %s\n", info.getPath() == null ? "--" : info.getPath());
+ mSdkLog.printf(" Path: %s\n",
+ info.getDataFolderPath() == null ? "--" : info.getDataFolderPath());
String error = info.getErrorMessage();
mSdkLog.printf(" Error: %s\n", error == null ? "Uknown error" : error);
@@ -923,8 +1010,7 @@ public class Main {
if (paramFolderPath != null) {
avdFolder = new File(paramFolderPath);
} else {
- avdFolder = new File(AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD,
- avdName + AvdManager.AVD_FOLDER_EXTENSION);
+ avdFolder = AvdInfo.getDefaultAvdFolder(avdManager, avdName);
}
// Validate skin is either default (empty) or NNNxMMM or a valid skin name.
@@ -981,15 +1067,20 @@ public class Main {
oldAvdInfo = avdManager.getAvd(avdName, false /*validAvdOnly*/);
}
+ // NOTE: need to update with command line processor selectivity
+
+ String preferredAbi = SdkConstants.ABI_ARMEABI;
@SuppressWarnings("unused") // newAvdInfo is never read, yet useful for debugging
AvdInfo newAvdInfo = avdManager.createAvd(avdFolder,
avdName,
target,
+ preferredAbi,
skin,
mSdkCommandLine.getParamSdCard(),
hardwareConfig,
- removePrevious,
mSdkCommandLine.getFlagSnapshot(),
+ removePrevious,
+ false, //edit existing
mSdkLog);
} catch (AndroidLocationException e) {
@@ -1045,7 +1136,7 @@ public class Main {
// check if paths are the same. Use File methods to account for OS idiosyncrasies.
try {
File f1 = new File(paramFolderPath).getCanonicalFile();
- File f2 = new File(info.getPath()).getCanonicalFile();
+ File f2 = new File(info.getDataFolderPath()).getCanonicalFile();
if (f1.equals(f2)) {
// same canonical path, so not actually a move
paramFolderPath = null;
@@ -1071,7 +1162,7 @@ public class Main {
File originalFolder = new File(
AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD,
info.getName() + AvdManager.AVD_FOLDER_EXTENSION);
- if (originalFolder.equals(info.getPath())) {
+ if (originalFolder.equals(info.getDataFolderPath())) {
try {
// The AVD is using the default data folder path based on the AVD name.
// That folder needs to be adjusted to use the new name.
@@ -1093,7 +1184,7 @@ public class Main {
}
File ini = info.getIniFile();
- if (ini.equals(AvdInfo.getIniFile(newName))) {
+ if (ini.equals(AvdInfo.getDefaultIniFile(avdManager, newName))) {
errorAndExit("The AVD file '%s' is in the way.", ini.getCanonicalPath());
return;
}
diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
index 5bb6c4e..fb15cb5 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
@@ -38,47 +38,49 @@ class SdkCommandLine extends CommandLineProcessor {
* or optional) for the given action.
*/
- public final static String VERB_LIST = "list";
- public final static String VERB_CREATE = "create";
- public final static String VERB_MOVE = "move";
- public final static String VERB_DELETE = "delete";
- public final static String VERB_UPDATE = "update";
-
- public static final String OBJECT_SDK = "sdk";
- public static final String OBJECT_AVD = "avd";
- public static final String OBJECT_AVDS = "avds";
- public static final String OBJECT_TARGET = "target";
- public static final String OBJECT_TARGETS = "targets";
- public static final String OBJECT_PROJECT = "project";
- public static final String OBJECT_TEST_PROJECT = "test-project";
- public static final String OBJECT_LIB_PROJECT = "lib-project";
- public static final String OBJECT_EXPORT_PROJECT = "export-project";
- public static final String OBJECT_ADB = "adb";
-
- public static final String ARG_ALIAS = "alias";
- public static final String ARG_ACTIVITY = "activity";
+ public final static String VERB_LIST = "list"; //$NON-NLS-1$
+ public final static String VERB_CREATE = "create"; //$NON-NLS-1$
+ public final static String VERB_MOVE = "move"; //$NON-NLS-1$
+ public final static String VERB_DELETE = "delete"; //$NON-NLS-1$
+ public final static String VERB_UPDATE = "update"; //$NON-NLS-1$
+
+ public static final String OBJECT_SDK = "sdk"; //$NON-NLS-1$
+ public static final String OBJECT_AVD = "avd"; //$NON-NLS-1$
+ public static final String OBJECT_AVDS = "avds"; //$NON-NLS-1$
+ public static final String OBJECT_TARGET = "target"; //$NON-NLS-1$
+ public static final String OBJECT_TARGETS = "targets"; //$NON-NLS-1$
+ public static final String OBJECT_PROJECT = "project"; //$NON-NLS-1$
+ public static final String OBJECT_TEST_PROJECT = "test-project"; //$NON-NLS-1$
+ public static final String OBJECT_LIB_PROJECT = "lib-project"; //$NON-NLS-1$
+ public static final String OBJECT_EXPORT_PROJECT = "export-project"; //$NON-NLS-1$
+ public static final String OBJECT_ADB = "adb"; //$NON-NLS-1$
+
+ public static final String ARG_ALIAS = "alias"; //$NON-NLS-1$
+ public static final String ARG_ACTIVITY = "activity"; //$NON-NLS-1$
public static final String KEY_ACTIVITY = ARG_ACTIVITY;
- public static final String KEY_PACKAGE = "package";
- public static final String KEY_MODE = "mode";
+ public static final String KEY_PACKAGE = "package"; //$NON-NLS-1$
+ public static final String KEY_MODE = "mode"; //$NON-NLS-1$
public static final String KEY_TARGET_ID = OBJECT_TARGET;
- public static final String KEY_NAME = "name";
- public static final String KEY_LIBRARY = "library";
- public static final String KEY_PATH = "path";
- public static final String KEY_FILTER = "filter";
- public static final String KEY_SKIN = "skin";
- public static final String KEY_SDCARD = "sdcard";
- public static final String KEY_FORCE = "force";
- public static final String KEY_RENAME = "rename";
- public static final String KEY_SUBPROJECTS = "subprojects";
- public static final String KEY_MAIN_PROJECT = "main";
- public static final String KEY_NO_UI = "no-ui";
- public static final String KEY_NO_HTTPS = "no-https";
- public static final String KEY_PROXY_PORT = "proxy-port";
- public static final String KEY_PROXY_HOST = "proxy-host";
- public static final String KEY_DRY_MODE = "dry-mode";
- public static final String KEY_OBSOLETE = "obsolete";
- public static final String KEY_SNAPSHOT = "snapshot";
+ public static final String KEY_NAME = "name"; //$NON-NLS-1$
+ public static final String KEY_LIBRARY = "library"; //$NON-NLS-1$
+ public static final String KEY_PATH = "path"; //$NON-NLS-1$
+ public static final String KEY_FILTER = "filter"; //$NON-NLS-1$
+ public static final String KEY_SKIN = "skin"; //$NON-NLS-1$
+ public static final String KEY_SDCARD = "sdcard"; //$NON-NLS-1$
+ public static final String KEY_FORCE = "force"; //$NON-NLS-1$
+ public static final String KEY_RENAME = "rename"; //$NON-NLS-1$
+ public static final String KEY_SUBPROJECTS = "subprojects"; //$NON-NLS-1$
+ public static final String KEY_MAIN_PROJECT = "main"; //$NON-NLS-1$
+ public static final String KEY_NO_UI = "no-ui"; //$NON-NLS-1$
+ public static final String KEY_NO_HTTPS = "no-https"; //$NON-NLS-1$
+ public static final String KEY_PROXY_PORT = "proxy-port"; //$NON-NLS-1$
+ public static final String KEY_PROXY_HOST = "proxy-host"; //$NON-NLS-1$
+ public static final String KEY_DRY_MODE = "dry-mode"; //$NON-NLS-1$
+ public static final String KEY_OBSOLETE = "obsolete"; //$NON-NLS-1$
+ public static final String KEY_SNAPSHOT = "snapshot"; //$NON-NLS-1$
+ public static final String KEY_COMPACT = "compact"; //$NON-NLS-1$
+ public static final String KEY_EOL_NULL = "null"; //$NON-NLS-1$
/**
* Action definitions for SdkManager command line.
@@ -104,6 +106,8 @@ class SdkCommandLine extends CommandLineProcessor {
{ VERB_LIST, OBJECT_TARGET,
"Lists existing targets.",
OBJECT_TARGETS },
+ { VERB_LIST, OBJECT_SDK,
+ "Lists remote SDK repository." },
{ VERB_CREATE, OBJECT_AVD,
"Creates a new Android Virtual Device." },
@@ -147,92 +151,139 @@ class SdkCommandLine extends CommandLineProcessor {
// The following defines the parameters of the actions defined in mAction.
+ // --- list avds ---
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_AVD, "c", KEY_COMPACT, //$NON-NLS-1$
+ "Compact output (suitable for scripts)", false);
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_AVD, "0", KEY_EOL_NULL, //$NON-NLS-1$
+ "Terminates lines with \\0 instead of \\n (e.g. for xargs -0). Only used by --" + KEY_COMPACT + ".",
+ false);
+
+ // --- list targets ---
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_TARGET, "c", KEY_COMPACT, //$NON-NLS-1$
+ "Compact output (suitable for scripts)", false);
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_TARGET, "0", KEY_EOL_NULL, //$NON-NLS-1$
+ "Terminates lines with \\0 instead of \\n (e.g. for xargs -0) Only used by --" + KEY_COMPACT + ".",
+ false);
+
// --- create avd ---
define(Mode.STRING, false,
- VERB_CREATE, OBJECT_AVD, "p", KEY_PATH,
+ VERB_CREATE, OBJECT_AVD, "p", KEY_PATH, //$NON-NLS-1$
"Directory where the new AVD will be created", null);
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_AVD, "n", KEY_NAME,
+ VERB_CREATE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$
"Name of the new AVD", null);
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID,
+ VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID, //$NON-NLS-1$
"Target ID of the new AVD", null);
define(Mode.STRING, false,
- VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN,
+ VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN, //$NON-NLS-1$
"Skin for the new AVD", null);
define(Mode.STRING, false,
- VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD,
+ VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD, //$NON-NLS-1$
"Path to a shared SD card image, or size of a new sdcard for the new AVD", null);
define(Mode.BOOLEAN, false,
- VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE,
+ VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE, //$NON-NLS-1$
"Forces creation (overwrites an existing AVD)", false);
define(Mode.BOOLEAN, false,
- VERB_CREATE, OBJECT_AVD, "a", KEY_SNAPSHOT,
+ VERB_CREATE, OBJECT_AVD, "a", KEY_SNAPSHOT, //$NON-NLS-1$
"Place a snapshots file in the AVD, to enable persistence.", false);
// --- delete avd ---
define(Mode.STRING, true,
- VERB_DELETE, OBJECT_AVD, "n", KEY_NAME,
+ VERB_DELETE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$
"Name of the AVD to delete", null);
// --- move avd ---
define(Mode.STRING, true,
- VERB_MOVE, OBJECT_AVD, "n", KEY_NAME,
+ VERB_MOVE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$
"Name of the AVD to move or rename", null);
define(Mode.STRING, false,
- VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME,
+ VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME, //$NON-NLS-1$
"New name of the AVD", null);
define(Mode.STRING, false,
- VERB_MOVE, OBJECT_AVD, "p", KEY_PATH,
+ VERB_MOVE, OBJECT_AVD, "p", KEY_PATH, //$NON-NLS-1$
"Path to the AVD's new directory", null);
// --- update avd ---
define(Mode.STRING, true,
- VERB_UPDATE, OBJECT_AVD, "n", KEY_NAME,
+ VERB_UPDATE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$
"Name of the AVD to update", null);
+ // --- list sdk ---
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_SDK, "u", KEY_NO_UI, //$NON-NLS-1$
+ "Displays list result on console (no GUI)", true);
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_SDK, "s", KEY_NO_HTTPS, //$NON-NLS-1$
+ "Uses HTTP instead of HTTPS (the default) for downloads", false);
+
+ define(Mode.STRING, false,
+ VERB_LIST, OBJECT_SDK, "", KEY_PROXY_PORT, //$NON-NLS-1$
+ "HTTP/HTTPS proxy port (overrides settings if defined)",
+ null);
+
+ define(Mode.STRING, false,
+ VERB_LIST, OBJECT_SDK, "", KEY_PROXY_HOST, //$NON-NLS-1$
+ "HTTP/HTTPS proxy host (overrides settings if defined)",
+ null);
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_SDK, "o", KEY_OBSOLETE, //$NON-NLS-1$
+ "Installs obsolete packages",
+ false);
+
// --- update sdk ---
define(Mode.BOOLEAN, false,
- VERB_UPDATE, OBJECT_SDK, "u", KEY_NO_UI,
+ VERB_UPDATE, OBJECT_SDK, "u", KEY_NO_UI, //$NON-NLS-1$
"Updates from command-line (does not display the GUI)", false);
define(Mode.BOOLEAN, false,
- VERB_UPDATE, OBJECT_SDK, "s", KEY_NO_HTTPS,
+ VERB_UPDATE, OBJECT_SDK, "s", KEY_NO_HTTPS, //$NON-NLS-1$
"Uses HTTP instead of HTTPS (the default) for downloads", false);
define(Mode.STRING, false,
- VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_PORT,
+ VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_PORT, //$NON-NLS-1$
"HTTP/HTTPS proxy port (overrides settings if defined)",
null);
define(Mode.STRING, false,
- VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_HOST,
+ VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_HOST, //$NON-NLS-1$
"HTTP/HTTPS proxy host (overrides settings if defined)",
null);
define(Mode.BOOLEAN, false,
- VERB_UPDATE, OBJECT_SDK, "f", KEY_FORCE,
+ VERB_UPDATE, OBJECT_SDK, "f", KEY_FORCE, //$NON-NLS-1$
"Forces replacement of a package or its parts, even if something has been modified",
false);
define(Mode.STRING, false,
- VERB_UPDATE, OBJECT_SDK, "t", KEY_FILTER,
+ VERB_UPDATE, OBJECT_SDK, "t", KEY_FILTER, //$NON-NLS-1$
"A filter that limits the update to the specified types of packages in the form of\n" +
"a comma-separated list of " + Arrays.toString(SdkRepoConstants.NODES),
null);
define(Mode.BOOLEAN, false,
- VERB_UPDATE, OBJECT_SDK, "o", KEY_OBSOLETE,
+ VERB_UPDATE, OBJECT_SDK, "o", KEY_OBSOLETE, //$NON-NLS-1$
"Installs obsolete packages",
false);
define(Mode.BOOLEAN, false,
- VERB_UPDATE, OBJECT_SDK, "n", KEY_DRY_MODE,
+ VERB_UPDATE, OBJECT_SDK, "n", KEY_DRY_MODE, //$NON-NLS-1$
"Simulates the update but does not download or install anything",
false);
@@ -242,7 +293,7 @@ class SdkCommandLine extends CommandLineProcessor {
This currently does not work, the alias build rules need to be fixed.
define(Mode.ENUM, true,
- VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE,
+ VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE, //$NON-NLS-1$
"Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS });
*/
define(Mode.STRING, true,
@@ -250,45 +301,44 @@ class SdkCommandLine extends CommandLineProcessor {
"p", KEY_PATH,
"The new project's directory", null);
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID,
+ VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$
"Target ID of the new project", null);
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE,
+ VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE, //$NON-NLS-1$
"Android package name for the application", null);
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY,
+ VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY, //$NON-NLS-1$
"Name of the default Activity that is created", null);
define(Mode.STRING, false,
- VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME,
+ VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$
"Project name", null);
// --- create test-project ---
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_TEST_PROJECT,
- "p", KEY_PATH,
+ VERB_CREATE, OBJECT_TEST_PROJECT, "p", KEY_PATH, //$NON-NLS-1$
"The new project's directory", null);
define(Mode.STRING, false,
- VERB_CREATE, OBJECT_TEST_PROJECT, "n", KEY_NAME,
+ VERB_CREATE, OBJECT_TEST_PROJECT, "n", KEY_NAME, //$NON-NLS-1$
"Project name", null);
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT,
- "Path to directory of the app under test, relative to the test project directory", null);
+ VERB_CREATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT, //$NON-NLS-1$
+ "Path to directory of the app under test, relative to the test project directory",
+ null);
// --- create lib-project ---
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_LIB_PROJECT,
- "p", KEY_PATH,
+ VERB_CREATE, OBJECT_LIB_PROJECT, "p", KEY_PATH, //$NON-NLS-1$
"The new project's directory", null);
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID,
+ VERB_CREATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$
"Target ID of the new project", null);
define(Mode.STRING, false,
- VERB_CREATE, OBJECT_LIB_PROJECT, "n", KEY_NAME,
+ VERB_CREATE, OBJECT_LIB_PROJECT, "n", KEY_NAME, //$NON-NLS-1$
"Project name", null);
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_LIB_PROJECT, "k", KEY_PACKAGE,
+ VERB_CREATE, OBJECT_LIB_PROJECT, "k", KEY_PACKAGE, //$NON-NLS-1$
"Android package name for the library", null);
// --- create export-project ---
@@ -296,74 +346,63 @@ class SdkCommandLine extends CommandLineProcessor {
* disabled until the feature is officially supported.
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_EXPORT_PROJECT,
- "p", KEY_PATH,
+ VERB_CREATE, OBJECT_EXPORT_PROJECT, "p", KEY_PATH, //$NON-NLS-1$
"Location path of new project", null);
define(Mode.STRING, false,
- VERB_CREATE, OBJECT_EXPORT_PROJECT, "n", KEY_NAME,
+ VERB_CREATE, OBJECT_EXPORT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$
"Project name", null);
define(Mode.STRING, true,
- VERB_CREATE, OBJECT_EXPORT_PROJECT, "k", KEY_PACKAGE,
+ VERB_CREATE, OBJECT_EXPORT_PROJECT, "k", KEY_PACKAGE, //$NON-NLS-1$
"Package name", null);
*/
// --- update project ---
define(Mode.STRING, true,
- VERB_UPDATE, OBJECT_PROJECT,
- "p", KEY_PATH,
+ VERB_UPDATE, OBJECT_PROJECT, "p", KEY_PATH, //$NON-NLS-1$
"The project's directory", null);
define(Mode.STRING, false,
- VERB_UPDATE, OBJECT_PROJECT,
- "t", KEY_TARGET_ID,
+ VERB_UPDATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$
"Target ID to set for the project", null);
define(Mode.STRING, false,
- VERB_UPDATE, OBJECT_PROJECT,
- "n", KEY_NAME,
+ VERB_UPDATE, OBJECT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$
"Project name", null);
define(Mode.BOOLEAN, false,
- VERB_UPDATE, OBJECT_PROJECT,
- "s", KEY_SUBPROJECTS,
+ VERB_UPDATE, OBJECT_PROJECT, "s", KEY_SUBPROJECTS, //$NON-NLS-1$
"Also updates any projects in sub-folders, such as test projects.", false);
define(Mode.STRING, false,
- VERB_UPDATE, OBJECT_PROJECT,
- "l", KEY_LIBRARY,
- "Directory of an Android library to add, relative to this project's directory", null);
+ VERB_UPDATE, OBJECT_PROJECT, "l", KEY_LIBRARY, //$NON-NLS-1$
+ "Directory of an Android library to add, relative to this project's directory",
+ null);
// --- update test project ---
define(Mode.STRING, true,
- VERB_UPDATE, OBJECT_TEST_PROJECT,
- "p", KEY_PATH,
+ VERB_UPDATE, OBJECT_TEST_PROJECT, "p", KEY_PATH, //$NON-NLS-1$
"The project's directory", null);
define(Mode.STRING, true,
- VERB_UPDATE, OBJECT_TEST_PROJECT,
- "m", KEY_MAIN_PROJECT,
+ VERB_UPDATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT, //$NON-NLS-1$
"Directory of the app under test, relative to the test project directory", null);
// --- update lib project ---
define(Mode.STRING, true,
- VERB_UPDATE, OBJECT_LIB_PROJECT,
- "p", KEY_PATH,
+ VERB_UPDATE, OBJECT_LIB_PROJECT, "p", KEY_PATH, //$NON-NLS-1$
"The project's directory", null);
define(Mode.STRING, false,
- VERB_UPDATE, OBJECT_LIB_PROJECT,
- "t", KEY_TARGET_ID,
+ VERB_UPDATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$
"Target ID to set for the project", null);
// --- update export project ---
/*
* disabled until the feature is officially supported.
define(Mode.STRING, true,
- VERB_UPDATE, OBJECT_EXPORT_PROJECT,
- "p", KEY_PATH,
+ VERB_UPDATE, OBJECT_EXPORT_PROJECT, "p", KEY_PATH, //$NON-NLS-1$
"Location path of the project", null);
define(Mode.STRING, false,
- VERB_UPDATE, OBJECT_EXPORT_PROJECT,
- "n", KEY_NAME,
+ VERB_UPDATE, OBJECT_EXPORT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$
"Project name", null);
define(Mode.BOOLEAN, false,
- VERB_UPDATE, OBJECT_EXPORT_PROJECT, "f", KEY_FORCE,
+ VERB_UPDATE, OBJECT_EXPORT_PROJECT, "f", KEY_FORCE, //$NON-NLS-1$
"Force replacing the build.xml file", false);
*/
}
@@ -464,9 +503,9 @@ class SdkCommandLine extends CommandLineProcessor {
// -- some helpers for update sdk flags
- /** Helper to retrieve the --force flag. */
- public boolean getFlagNoUI() {
- return ((Boolean) getValue(null, null, KEY_NO_UI)).booleanValue();
+ /** Helper to retrieve the --no-ui flag. */
+ public boolean getFlagNoUI(String verb) {
+ return ((Boolean) getValue(verb, null, KEY_NO_UI)).booleanValue();
}
/** Helper to retrieve the --no-https flag. */
@@ -490,12 +529,24 @@ class SdkCommandLine extends CommandLineProcessor {
}
/** Helper to retrieve the --proxy-host value. */
- public String getProxyHost() {
+ public String getParamProxyHost() {
return ((String) getValue(null, null, KEY_PROXY_HOST));
}
/** Helper to retrieve the --proxy-port value. */
- public String getProxyPort() {
+ public String getParamProxyPort() {
return ((String) getValue(null, null, KEY_PROXY_PORT));
}
+
+ // -- some helpers for list avds and list targets flags
+
+ /** Helper to retrieve the --compact value. */
+ public boolean getFlagCompact() {
+ return ((Boolean) getValue(null, null, KEY_COMPACT)).booleanValue();
+ }
+
+ /** Helper to retrieve the --null value. */
+ public boolean getFlagEolNull() {
+ return ((Boolean) getValue(null, null, KEY_EOL_NULL)).booleanValue();
+ }
}
diff --git a/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java b/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java
index cb2f981..4c72e1e 100755
--- a/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java
@@ -70,7 +70,7 @@ public class AboutPage extends Composite {
"Revision %1$s\n" +
"Add-on XML Schema #%2$d\n" +
"Repository XML Schema #%3$d\n" +
- "Copyright (C) 2009-2010 The Android Open Source Project.",
+ "Copyright (C) 2009-2011 The Android Open Source Project.",
getRevision(),
SdkAddonConstants.NS_LATEST_VERSION,
SdkRepoConstants.NS_LATEST_VERSION));
diff --git a/androidprefs/src/Android.mk b/sdkmanager/app/tests/Android.mk
index ddc0aa6..4f67370 100644
--- a/androidprefs/src/Android.mk
+++ b/sdkmanager/app/tests/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
+# Copyright (C) 2011 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.
@@ -12,13 +11,18 @@
# 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)
+# Only compile source java files in this lib.
LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := androidprefs
+LOCAL_MODULE := sdkmanager-tests
+LOCAL_MODULE_TAGS := optional
-include $(BUILD_HOST_JAVA_LIBRARY)
+LOCAL_JAVA_LIBRARIES := sdkmanager sdklib-tests junit
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java
index 4ae107c..a5a8289 100644
--- a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java
+++ b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java
@@ -16,59 +16,57 @@
package com.android.sdkmanager;
-import static java.io.File.createTempFile;
-
+import com.android.io.FileWrapper;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.SdkManager;
-import com.android.sdklib.internal.avd.AvdManager;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.project.ProjectProperties;
-import com.android.sdklib.io.FileWrapper;
-import com.android.sdklib.mock.MockLog;
import java.io.File;
import java.util.Map;
-import junit.framework.TestCase;
-
-public class AvdManagerTest extends TestCase {
+public class AvdManagerTest extends SdkManagerTestCase {
- private AvdManager mAvdManager;
- private SdkManager mSdkManager;
- private MockLog mLog;
- private File mFakeSdk;
- private File mAvdFolder;
private IAndroidTarget mTarget;
+ private File mAvdFolder;
@Override
public void setUp() throws Exception {
- mLog = new MockLog();
- mFakeSdk = SdkManagerTestUtil.makeFakeSdk(createTempFile(this.getClass().getSimpleName(), null));
- mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog);
- assertNotNull("sdkManager location was invalid", mSdkManager);
+ super.setUp();
- mAvdManager = new AvdManager(mSdkManager, mLog);
- mAvdFolder = new File(mFakeSdk, "avdData");
- mTarget = mSdkManager.getTargets()[0];
+ mTarget = getSdkManager().getTargets()[0];
+ mAvdFolder = AvdInfo.getDefaultAvdFolder(getAvdManager(), getName());
}
@Override
public void tearDown() throws Exception {
- SdkManagerTestUtil.deleteDir(mFakeSdk);
+ super.tearDown();
}
public void testCreateAvdWithoutSnapshot() {
- mAvdManager.createAvd(
- mAvdFolder, this.getName(), mTarget, null, null, null, false, false, mLog);
- assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0\n]",
- mLog.toString());
+ getAvdManager().createAvd(
+ mAvdFolder,
+ this.getName(),
+ mTarget,
+ SdkConstants.ABI_ARMEABI,
+ null, // skinName
+ null, // sdName
+ null, // properties
+ false, // createSnapshot
+ false, // removePrevious
+ false, // editExisting
+ getLog());
+
+ assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0, ARM (armeabi) processor\n]",
+ getLog().toString());
assertTrue("Expected config.ini in " + mAvdFolder,
new File(mAvdFolder, "config.ini").exists());
Map<String, String> map = ProjectProperties.parsePropertyFile(
- new FileWrapper(mAvdFolder, "config.ini"), mLog);
+ new FileWrapper(mAvdFolder, "config.ini"), getLog());
assertEquals("HVGA", map.get("skin.name"));
- assertEquals("platforms/v0_0/skins/HVGA", map.get("skin.path"));
- assertEquals("platforms/v0_0/images/", map.get("image.sysdir.1"));
+ assertEquals("platforms/v0_0/skins/HVGA", map.get("skin.path").replace(File.separatorChar, '/'));
+ assertEquals("platforms/v0_0/images/", map.get("image.sysdir.1").replace(File.separatorChar, '/'));
assertEquals(null, map.get("snapshot.present"));
assertTrue("Expected userdata.img in " + mAvdFolder,
new File(mAvdFolder, "userdata.img").exists());
@@ -77,15 +75,26 @@ public class AvdManagerTest extends TestCase {
}
public void testCreateAvdWithSnapshot() {
- mAvdManager.createAvd(
- mAvdFolder, this.getName(), mTarget, null, null, null, false, true, mLog);
- assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0\n]",
- mLog.toString());
+ getAvdManager().createAvd(
+ mAvdFolder,
+ this.getName(),
+ mTarget,
+ SdkConstants.ABI_ARMEABI,
+ null, // skinName
+ null, // sdName
+ null, // properties
+ true, // createSnapshot
+ false, // removePrevious
+ false, // editExisting
+ getLog());
+
+ assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0, ARM (armeabi) processor\n]",
+ getLog().toString());
assertTrue("Expected snapshots.img in " + mAvdFolder,
new File(mAvdFolder, "snapshots.img").exists());
Map<String, String> map = ProjectProperties.parsePropertyFile(
- new FileWrapper(mAvdFolder, "config.ini"), mLog);
+ new FileWrapper(mAvdFolder, "config.ini"), getLog());
assertEquals("true", map.get("snapshot.present"));
}
}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
index 29516e3..4a17e32 100644
--- a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
+++ b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
@@ -17,60 +17,63 @@
package com.android.sdkmanager;
-import static java.io.File.createTempFile;
-
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.SdkManager;
-import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.mock.MockLog;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.avd.AvdInfo;
+import com.android.sdklib.repository.SdkAddonConstants;
+import com.android.sdklib.repository.SdkRepoConstants;
+import com.android.util.Pair;
import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.TreeSet;
-import junit.framework.TestCase;
-
-public class MainTest extends TestCase {
+public class MainTest extends SdkManagerTestCase {
- private File mFakeSdk;
- private MockLog mLog;
- private SdkManager mSdkManager;
- private AvdManager mAvdManager;
- private File mAvdFolder;
private IAndroidTarget mTarget;
- private File fakeSdkDir;
+ private File mAvdFolder;
@Override
public void setUp() throws Exception {
- mLog = new MockLog();
- fakeSdkDir = createTempFile(this.getClass().getSimpleName() + "_" + this.getName(), null);
- mFakeSdk = SdkManagerTestUtil.makeFakeSdk(fakeSdkDir);
- mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog);
- assertNotNull("sdkManager location was invalid", mSdkManager);
-
- mAvdManager = new AvdManager(mSdkManager, mLog);
- mAvdFolder = new File(mFakeSdk, "avdData");
- mTarget = mSdkManager.getTargets()[0];
+ super.setUp();
+
+ mTarget = getSdkManager().getTargets()[0];
+ mAvdFolder = AvdInfo.getDefaultAvdFolder(getAvdManager(), getName());
}
@Override
public void tearDown() throws Exception {
- SdkManagerTestUtil.deleteDir(mFakeSdk);
+ super.tearDown();
}
- public void txestDisplayEmptyAvdList() {
+ public void testDisplayEmptyAvdList() {
Main main = new Main();
- main.setLogger(mLog);
- mLog.clear();
- main.displayAvdList(mAvdManager);
- assertEquals("P Available Android Virtual Devices:\n", mLog.toString());
+ main.setLogger(getLog());
+ getLog().clear();
+ main.displayAvdList(getAvdManager());
+ assertEquals("[P Available Android Virtual Devices:\n]", getLog().toString());
}
public void testDisplayAvdListOfOneNonSnapshot() {
Main main = new Main();
- main.setLogger(mLog);
- mAvdManager.createAvd(
- mAvdFolder, this.getName(), mTarget, null, null, null, false, false, mLog);
- mLog.clear();
- main.displayAvdList(mAvdManager);
+ main.setLogger(getLog());
+ getAvdManager().createAvd(
+ mAvdFolder,
+ this.getName(),
+ mTarget,
+ SdkConstants.ABI_ARMEABI,
+ null, // skinName
+ null, // sdName
+ null, // properties
+ false, // createSnapshot
+ false, // removePrevious
+ false, // editExisting
+ getLog());
+
+ getLog().clear();
+ main.displayAvdList(getAvdManager());
assertEquals(
"[P Available Android Virtual Devices:\n"
+ ", P Name: " + this.getName() + "\n"
@@ -78,16 +81,28 @@ public class MainTest extends TestCase {
+ ", P Target: Android 0.0 (API level 0)\n"
+ ", P Skin: HVGA\n"
+ "]",
- mLog.toString());
+ getLog().toString());
}
public void testDisplayAvdListOfOneSnapshot() {
Main main = new Main();
- main.setLogger(mLog);
- mAvdManager.createAvd(
- mAvdFolder, this.getName(), mTarget, null, null, null, false, true, mLog);
- mLog.clear();
- main.displayAvdList(mAvdManager);
+ main.setLogger(getLog());
+
+ getAvdManager().createAvd(
+ mAvdFolder,
+ this.getName(),
+ mTarget,
+ SdkConstants.ABI_ARMEABI,
+ null, // skinName
+ null, // sdName
+ null, // properties
+ true, // createSnapshot
+ false, // removePrevious
+ false, // editExisting
+ getLog());
+
+ getLog().clear();
+ main.displayAvdList(getAvdManager());
assertEquals(
"[P Available Android Virtual Devices:\n"
+ ", P Name: " + this.getName() + "\n"
@@ -96,6 +111,83 @@ public class MainTest extends TestCase {
+ ", P Skin: HVGA\n"
+ ", P Snapshot: true\n"
+ "]",
- mLog.toString());
+ getLog().toString());
+ }
+
+ public void testCheckFilterValues() {
+ // These are the values we expect checkFilterValues() to match.
+ String[] expectedValues = {
+ "platform",
+ "tool",
+ "platform-tool",
+ "doc",
+ "sample",
+ "add-on",
+ "extra"
+ };
+
+ Set<String> expectedSet = new TreeSet<String>(Arrays.asList(expectedValues));
+
+ // First check the values are actually defined in the proper arrays
+ // in the Sdk*Constants.NODES
+ for (String node : SdkRepoConstants.NODES) {
+ assertTrue(
+ String.format(
+ "Error: value '%1$s' from SdkRepoConstants.NODES should be used in unit-test",
+ node),
+ expectedSet.contains(node));
+ }
+ for (String node : SdkAddonConstants.NODES) {
+ assertTrue(
+ String.format(
+ "Error: value '%1$s' from SdkAddonConstants.NODES should be used in unit-test",
+ node),
+ expectedSet.contains(node));
+ }
+
+ // Now check none of these values are NOT present in the NODES arrays
+ for (String node : SdkRepoConstants.NODES) {
+ expectedSet.remove(node);
+ }
+ for (String node : SdkAddonConstants.NODES) {
+ expectedSet.remove(node);
+ }
+ assertTrue(
+ String.format(
+ "Error: values %1$s are missing from Sdk[Repo|Addons]Constants.NODES",
+ Arrays.toString(expectedSet.toArray())),
+ expectedSet.isEmpty());
+
+ // We're done with expectedSet now
+ expectedSet = null;
+
+ // Finally check that checkFilterValues accepts all these values, one by one.
+ Main main = new Main();
+ main.setLogger(getLog());
+
+ for (int step = 0; step < 3; step++) {
+ for (String value : expectedValues) {
+ switch(step) {
+ // step 0: use value as-is
+ case 1:
+ // add some whitespace before and after
+ value = " " + value + " ";
+ break;
+ case 2:
+ // same with some empty arguments that should get ignored
+ value = " ," + value + " , ";
+ break;
+ }
+
+ Pair<String, ArrayList<String>> result = main.checkFilterValues(value);
+ assertNull(
+ String.format("Expected error to be null for value '%1$s', got: %2$s",
+ value, result.getFirst()),
+ result.getFirst());
+ assertEquals(
+ String.format("[%1$s]", value.replace(',', ' ').trim()),
+ Arrays.toString(result.getSecond().toArray()));
+ }
+ }
}
}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java
new file mode 100755
index 0000000..9fdd852
--- /dev/null
+++ b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2011 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.sdkmanager;
+
+
+import com.android.prefs.AndroidLocation;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
+import com.android.sdklib.ISdkLog;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.SdkManager;
+import com.android.sdklib.internal.avd.AvdManager;
+import com.android.sdklib.mock.MockLog;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+/**
+ * Test case that allocates a temporary SDK, a temporary AVD base folder
+ * with an SdkManager and an AvdManager that points to them.
+ */
+public abstract class SdkManagerTestCase extends TestCase {
+
+ private File mFakeSdk;
+ private MockLog mLog;
+ private SdkManager mSdkManager;
+ private TmpAvdManager mAvdManager;
+
+ /** Returns the {@link MockLog} for this test case. */
+ public MockLog getLog() {
+ return mLog;
+ }
+
+ /** Returns the {@link SdkManager} for this test case. */
+ public SdkManager getSdkManager() {
+ return mSdkManager;
+ }
+
+ /** Returns the {@link AvdManager} for this test case. */
+ public TmpAvdManager getAvdManager() {
+ return mAvdManager;
+ }
+
+ /**
+ * Sets up a {@link MockLog}, a fake SDK in a temporary directory
+ * and an AVD Manager pointing to an initially-empty AVD directory.
+ */
+ @Override
+ public void setUp() throws Exception {
+ mLog = new MockLog();
+ mFakeSdk = makeFakeSdk();
+ mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog);
+ assertNotNull("SdkManager location was invalid", mSdkManager);
+
+ mAvdManager = new TmpAvdManager(mSdkManager, mLog);
+ }
+
+ /**
+ * Removes the temporary SDK and AVD directories.
+ */
+ @Override
+ public void tearDown() throws Exception {
+ deleteDir(mFakeSdk);
+ }
+
+ /**
+ * An {@link AvdManager} that uses a temporary directory
+ * located <em>inside</em> the SDK directory for testing.
+ * The AVD list should be initially empty.
+ */
+ protected static class TmpAvdManager extends AvdManager {
+
+ /*
+ * Implementation detail:
+ * - When the super.AvdManager constructor is invoked, it will invoke
+ * the buildAvdFilesList() to fill the initial AVD list, which will in
+ * turn call getBaseAvdFolder().
+ * - That's why mTmpAvdRoot is initialized in getAvdRoot() rather than
+ * in the constructor, since we can't initialize fields before the super()
+ * call.
+ */
+
+ /**
+ * AVD Root, initialized "lazily" when the AVD root is first requested.
+ */
+ private File mTmpAvdRoot;
+
+ public TmpAvdManager(SdkManager sdkManager, ISdkLog log) throws AndroidLocationException {
+ super(sdkManager, log);
+ }
+
+ @Override
+ public String getBaseAvdFolder() throws AndroidLocationException {
+ if (mTmpAvdRoot == null) {
+ mTmpAvdRoot = new File(getSdkManager().getLocation(), "tmp_avds");
+ mTmpAvdRoot.mkdirs();
+ }
+ return mTmpAvdRoot.getAbsolutePath();
+ }
+ }
+
+ /**
+ * Build enough of a skeleton SDK to make the tests pass.
+ * <p/>
+ * Ideally this wouldn't touch the file system but the current
+ * structure of the SdkManager and AvdManager makes this difficult.
+ *
+ * @return Path to the temporary SDK root
+ * @throws IOException
+ */
+ private File makeFakeSdk() throws IOException {
+
+ File tmpFile = File.createTempFile(
+ this.getClass().getSimpleName() + '_' + this.getName(), null);
+ tmpFile.delete();
+ tmpFile.mkdirs();
+
+ AndroidLocation.resetFolder();
+ System.setProperty("user.home", tmpFile.getAbsolutePath());
+ File addonsDir = new File(tmpFile, SdkConstants.FD_ADDONS);
+ addonsDir.mkdir();
+ File toolsLibEmuDir = new File(tmpFile, SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + "emulator");
+ toolsLibEmuDir.mkdirs();
+ new File(toolsLibEmuDir, "snapshots.img").createNewFile();
+ File platformsDir = new File(tmpFile, SdkConstants.FD_PLATFORMS);
+
+ // Creating a fake target here on down
+ File targetDir = new File(platformsDir, "v0_0");
+ targetDir.mkdirs();
+ new File(targetDir, SdkConstants.FN_FRAMEWORK_LIBRARY).createNewFile();
+ new File(targetDir, SdkConstants.FN_FRAMEWORK_AIDL).createNewFile();
+ new File(targetDir, SdkConstants.FN_SOURCE_PROP).createNewFile();
+ File buildProp = new File(targetDir, SdkConstants.FN_BUILD_PROP);
+ FileWriter out = new FileWriter(buildProp);
+ out.write(SdkManager.PROP_VERSION_RELEASE + "=0.0\n");
+ out.write(SdkManager.PROP_VERSION_SDK + "=0\n");
+ out.write(SdkManager.PROP_VERSION_CODENAME + "=REL\n");
+ out.close();
+ File imagesDir = new File(targetDir, "images");
+ imagesDir.mkdirs();
+ new File(imagesDir, "userdata.img").createNewFile();
+ File skinsDir = new File(targetDir, "skins");
+ File hvgaDir = new File(skinsDir, "HVGA");
+ hvgaDir.mkdirs();
+ return tmpFile;
+ }
+
+ /**
+ * Recursive delete directory. Mostly for fake SDKs.
+ *
+ * @param root directory to delete
+ */
+ private void deleteDir(File root) {
+ if (root.exists()) {
+ for (File file : root.listFiles()) {
+ if (file.isDirectory()) {
+ deleteDir(file);
+ } else {
+ file.delete();
+ }
+ }
+ root.delete();
+ }
+ }
+
+}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java
deleted file mode 100644
index 96efb5c..0000000
--- a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.android.sdkmanager;
-
-import com.android.prefs.AndroidLocation;
-import com.android.sdklib.SdkConstants;
-import com.android.sdklib.SdkManager;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-
-public class SdkManagerTestUtil {
- /**
- * Build enough of a skeleton SDK to make the tests pass.
- *<p>
- * Ideally this wouldn't touch the file system, but I'm not inclined to
- * fiddle around with mock file systems just at the moment.
- *
- * @return an sdk manager to a fake sdk
- * @throws IOException
- */
- public static File makeFakeSdk(File fakeSdk) throws IOException {
- fakeSdk.delete();
- fakeSdk.mkdirs();
- AndroidLocation.resetFolder();
- System.setProperty("user.home", fakeSdk.getAbsolutePath());
- File addonsDir = new File(fakeSdk, SdkConstants.FD_ADDONS);
- addonsDir.mkdir();
- File toolsLibEmuDir = new File(fakeSdk, SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + "emulator");
- toolsLibEmuDir.mkdirs();
- new File(toolsLibEmuDir, "snapshots.img").createNewFile();
- File platformsDir = new File(fakeSdk, SdkConstants.FD_PLATFORMS);
-
- // Creating a fake target here on down
- File targetDir = new File(platformsDir, "v0_0");
- targetDir.mkdirs();
- new File(targetDir, SdkConstants.FN_FRAMEWORK_LIBRARY).createNewFile();
- new File(targetDir, SdkConstants.FN_FRAMEWORK_AIDL).createNewFile();
- new File(targetDir, SdkConstants.FN_SOURCE_PROP).createNewFile();
- File buildProp = new File(targetDir, SdkConstants.FN_BUILD_PROP);
- FileWriter out = new FileWriter(buildProp);
- out.write(SdkManager.PROP_VERSION_RELEASE + "=0.0\n");
- out.write(SdkManager.PROP_VERSION_SDK + "=0\n");
- out.write(SdkManager.PROP_VERSION_CODENAME + "=REL\n");
- out.close();
- File imagesDir = new File(targetDir, "images");
- imagesDir.mkdirs();
- new File(imagesDir, "userdata.img").createNewFile();
- File skinsDir = new File(targetDir, "skins");
- File hvgaDir = new File(skinsDir, "HVGA");
- hvgaDir.mkdirs();
- return fakeSdk;
- }
-
- /**
- * Recursive delete directory. Mostly for fake SDKs.
- *
- * @param root directory to delete
- */
- public static void deleteDir(File root) {
- if (root.exists()) {
- for (File file : root.listFiles()) {
- if (file.isDirectory()) {
- deleteDir(file);
- } else {
- file.delete();
- }
- }
- root.delete();
- }
- }
-
-}
diff --git a/sdkmanager/libs/sdklib/.classpath b/sdkmanager/libs/sdklib/.classpath
index 174a804..7cabaa0 100644
--- a/sdkmanager/libs/sdklib/.classpath
+++ b/sdkmanager/libs/sdklib/.classpath
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="tests"/>
+ <classpathentry kind="src" path="tests/src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/commons-compress/commons-compress-1.0.jar"/>
<classpathentry combineaccessrules="false" kind="src" path="/common"/>
<classpathentry kind="output" path="bin"/>
-</classpath>
+</classpath> \ No newline at end of file
diff --git a/sdkmanager/libs/sdklib/.settings/org.eclipse.jdt.core.prefs b/sdkmanager/libs/sdklib/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..cba2e98
--- /dev/null
+++ b/sdkmanager/libs/sdklib/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 16 15:10:56 PDT 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/sdkmanager/libs/sdklib/Android.mk b/sdkmanager/libs/sdklib/Android.mk
index 509c573..7ed009c 100644
--- a/sdkmanager/libs/sdklib/Android.mk
+++ b/sdkmanager/libs/sdklib/Android.mk
@@ -13,5 +13,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-SDKLIB_LOCAL_DIR := $(call my-dir)
-include $(SDKLIB_LOCAL_DIR)/src/Android.mk
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_JAR_MANIFEST := manifest.txt
+
+# IMPORTANT: if you add a new dependency here, please make sure
+# to also check the following files:
+# sdkmanager/sdklib/manifest.txt
+# sdkmanager/app/etc/android.bat
+LOCAL_JAVA_LIBRARIES := \
+ androidprefs \
+ common \
+ commons-compress-1.0
+
+LOCAL_MODULE := sdklib
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/sdkmanager/libs/sdklib/NOTICE b/sdkmanager/libs/sdklib/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/sdkmanager/libs/sdklib/NOTICE
@@ -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/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
index f3da39c..866d5b6 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
@@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.ArrayList;
/**
* Represents an add-on target in the SDK.
@@ -67,6 +68,7 @@ final class AddOnTarget implements IAndroidTarget {
private final String mLocation;
private final PlatformTarget mBasePlatform;
private final String mName;
+ private String[] mAbis;
private final String mVendor;
private final int mRevision;
private final String mDescription;
@@ -74,6 +76,7 @@ final class AddOnTarget implements IAndroidTarget {
private String mDefaultSkin;
private IOptionalLibrary[] mLibraries;
private int mVendorId = NO_USB_ID;
+ private boolean mAbiCompatibilityMode;
/**
* Creates a new add-on
@@ -82,12 +85,13 @@ final class AddOnTarget implements IAndroidTarget {
* @param vendor the vendor name of the add-on
* @param revision the revision of the add-on
* @param description the add-on description
+ * @param abis list of supported abis
* @param libMap A map containing the optional libraries. The map key is the fully-qualified
* library name. The value is a 2 string array with the .jar filename, and the description.
* @param basePlatform the platform the add-on is extending.
*/
AddOnTarget(String location, String name, String vendor, int revision, String description,
- Map<String, String[]> libMap, PlatformTarget basePlatform) {
+ String[] abis, Map<String, String[]> libMap, PlatformTarget basePlatform) {
if (location.endsWith(File.separator) == false) {
location = location + File.separator;
}
@@ -99,6 +103,14 @@ final class AddOnTarget implements IAndroidTarget {
mDescription = description;
mBasePlatform = basePlatform;
+ //set compatibility mode
+ if (abis.length > 0) {
+ mAbis = abis;
+ } else {
+ mAbiCompatibilityMode = true;
+ mAbis = new String[] { SdkConstants.ABI_ARMEABI };
+ }
+
// handle the optional libraries.
if (libMap != null) {
mLibraries = new IOptionalLibrary[libMap.size()];
@@ -121,6 +133,25 @@ final class AddOnTarget implements IAndroidTarget {
return mName;
}
+ /**
+ * Return the full path for images
+ * @param abiType type of the abi
+ * @return complete path where the image files are located
+ */
+ public String getImagePath(String abiType) {
+
+ if (mAbiCompatibilityMode) {
+ // Use legacy directory structure if only arm
+ return mLocation + SdkConstants.OS_IMAGES_FOLDER;
+ } else {
+ return mLocation + SdkConstants.OS_IMAGES_FOLDER + abiType + File.separator;
+ }
+ }
+
+ public String[] getAbiList() {
+ return mAbis;
+ }
+
public String getVendor() {
return mVendor;
}
@@ -160,8 +191,6 @@ final class AddOnTarget implements IAndroidTarget {
public String getPath(int pathId) {
switch (pathId) {
- case IMAGES:
- return mLocation + SdkConstants.OS_IMAGES_FOLDER;
case SKINS:
return mLocation + SdkConstants.OS_SKINS_FOLDER;
case DOCS:
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
index c0dcaa7..2ca7763 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
@@ -29,8 +29,6 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> {
public final static int ANDROID_JAR = 1;
/** OS Path to the "framework.aidl" file. */
public final static int ANDROID_AIDL = 2;
- /** OS Path to "images" folder which contains the emulator system images. */
- public final static int IMAGES = 3;
/** OS Path to the "samples" folder which contains sample projects. */
public final static int SAMPLES = 4;
/** OS Path to the "skins" folder which contains the emulator skins. */
@@ -227,6 +225,16 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> {
int getUsbVendorId();
/**
+ * Returns array of permitted processor architectures
+ */
+ public String[] getAbiList();
+
+ /**
+ * Returns string to append to images directory for current ProcessorType
+ */
+ public String getImagePath(String abiType);
+
+ /**
* Returns whether the given target is compatible with the receiver.
* <p/>
* This means that a project using the receiver's target can run on the given target.
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
index 6aeeade..62720e7 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
@@ -21,6 +21,7 @@ import com.android.sdklib.util.SparseArray;
import java.io.File;
import java.util.Collections;
import java.util.Map;
+import java.util.ArrayList;
/**
* Represents a platform target in the SDK.
@@ -43,21 +44,25 @@ final class PlatformTarget implements IAndroidTarget {
private final Map<String, String> mProperties;
private final SparseArray<String> mPaths = new SparseArray<String>();
private String[] mSkins;
+ private String[] mAbis;
+ private boolean mAbiCompatibilityMode;
/**
* Creates a Platform target.
* @param sdkOsPath the root folder of the SDK
* @param platformOSPath the root folder of the platform component
- * @param properties the platform properties
* @param apiLevel the API Level
* @param codeName the codename. can be null.
* @param versionName the version name of the platform.
* @param revision the revision of the platform component.
+ * @param abis the list of supported abis
+ * @param properties the platform properties
*/
@SuppressWarnings("deprecation")
- PlatformTarget(String sdkOsPath, String platformOSPath, Map<String, String> properties,
- int apiLevel, String codeName, String versionName, int revision) {
+ PlatformTarget(String sdkOsPath, String platformOSPath, int apiLevel,
+ String codeName, String versionName, int revision, String[] abis,
+ Map<String, String> properties) {
if (platformOSPath.endsWith(File.separator) == false) {
platformOSPath = platformOSPath + File.separator;
}
@@ -79,7 +84,6 @@ final class PlatformTarget implements IAndroidTarget {
mPaths.put(ANDROID_AIDL, mRootFolderOsPath + SdkConstants.FN_FRAMEWORK_AIDL);
mPaths.put(ANDROID_RS, mRootFolderOsPath + SdkConstants.OS_FRAMEWORK_RS);
mPaths.put(ANDROID_RS_CLANG, mRootFolderOsPath + SdkConstants.OS_FRAMEWORK_RS_CLANG);
- mPaths.put(IMAGES, mRootFolderOsPath + SdkConstants.OS_IMAGES_FOLDER);
mPaths.put(SAMPLES, mRootFolderOsPath + SdkConstants.OS_PLATFORM_SAMPLES_FOLDER);
mPaths.put(SKINS, mRootFolderOsPath + SdkConstants.OS_SKINS_FOLDER);
mPaths.put(TEMPLATES, mRootFolderOsPath + SdkConstants.OS_PLATFORM_TEMPLATES_FOLDER);
@@ -112,6 +116,36 @@ final class PlatformTarget implements IAndroidTarget {
SdkConstants.FN_DX);
mPaths.put(DX_JAR, sdkOsPath + SdkConstants.OS_SDK_PLATFORM_TOOLS_LIB_FOLDER +
SdkConstants.FN_DX_JAR);
+
+ //set compatibility mode, abis length would be 0 for older APIs
+ if (abis.length > 0) {
+ mAbis = abis;
+ } else {
+ mAbiCompatibilityMode = true;
+ mAbis = new String[] { SdkConstants.ABI_ARMEABI };
+ }
+
+ }
+
+ /**
+ * Return the full path for images
+ * @param abiType type of the abi
+ * @return complete path where the image files are located
+ */
+ public String getImagePath(String abiType) {
+ if (mAbiCompatibilityMode) {
+ // Use legacy directory structure if only arm is supported
+ return mRootFolderOsPath + SdkConstants.OS_IMAGES_FOLDER;
+ } else {
+ return mRootFolderOsPath + SdkConstants.OS_IMAGES_FOLDER + abiType + File.separator;
+ }
+ }
+
+ /**
+ * Retrieve and return the list of abis
+ */
+ public String[] getAbiList() {
+ return mAbis;
}
public String getLocation() {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index 37265b1..f75d3a9 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -16,6 +16,8 @@
package com.android.sdklib;
+import com.android.AndroidConstants;
+
import java.io.File;
/**
@@ -130,9 +132,13 @@ public final class SdkConstants {
public final static String FN_ADB = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
"adb.exe" : "adb"; //$NON-NLS-1$ //$NON-NLS-2$
- /** emulator executable (with extension for the current OS) */
- public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
- "emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
+ /** emulator executable (_WITHOUT_ extension for the current OS) */
+ public final static String FN_EMULATOR =
+ "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ /** emulator executable extension for the current OS */
+ public final static String FN_EMULATOR_EXTENSION = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
+ ".exe" : ""; //$NON-NLS-1$ //$NON-NLS-2$
/** zipalign executable (with extension for the current OS) */
public final static String FN_ZIPALIGN = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
@@ -180,28 +186,6 @@ public final class SdkConstants {
public final static String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$
/** Default output folder name, i.e. "bin" */
public final static String FD_OUTPUT = "bin"; //$NON-NLS-1$
- /** Default anim resource folder name, i.e. "anim" */
- public final static String FD_ANIM = "anim"; //$NON-NLS-1$
- /** Default animator resource folder name, i.e. "animator" */
- public final static String FD_ANIMATOR = "animator"; //$NON-NLS-1$
- /** Default color resource folder name, i.e. "color" */
- public final static String FD_COLOR = "color"; //$NON-NLS-1$
- /** Default drawable resource folder name, i.e. "drawable" */
- public final static String FD_DRAWABLE = "drawable"; //$NON-NLS-1$
- /** Default interpolator resource folder name, i.e. "interpolator" */
- public final static String FD_INTERPOLATOR = "interpolator"; //$NON-NLS-1$
- /** Default layout resource folder name, i.e. "layout" */
- public final static String FD_LAYOUT = "layout"; //$NON-NLS-1$
- /** Default menu resource folder name, i.e. "menu" */
- public final static String FD_MENU = "menu"; //$NON-NLS-1$
- /** Default menu resource folder name, i.e. "mipmap" */
- public final static String FD_MIPMAP = "mipmap"; //$NON-NLS-1$
- /** Default values resource folder name, i.e. "values" */
- public final static String FD_VALUES = "values"; //$NON-NLS-1$
- /** Default xml resource folder name, i.e. "xml" */
- public final static String FD_XML = "xml"; //$NON-NLS-1$
- /** Default raw resource folder name, i.e. "raw" */
- public final static String FD_RAW = "raw"; //$NON-NLS-1$
/** proguard output folder for mapping, etc.. files */
public final static String FD_PROGUARD = "proguard"; //$NON-NLS-1$
@@ -223,6 +207,10 @@ public final class SdkConstants {
public static final String FD_DOCS_REFERENCE = "reference";
/** Name of the SDK images folder. */
public final static String FD_IMAGES = "images";
+ /** Name of the processors to support. */
+ public final static String ABI_ARMEABI = "armeabi";
+ public final static String ABI_INTEL_ATOM = "x86";
+
/** Name of the SDK skins folder. */
public final static String FD_SKINS = "skins";
/** Name of the SDK samples folder. */
@@ -336,11 +324,13 @@ public final class SdkConstants {
/** Path of the attrs.xml file relative to a platform folder. */
public final static String OS_PLATFORM_ATTRS_XML =
- OS_PLATFORM_RESOURCES_FOLDER + FD_VALUES + File.separator + FN_ATTRS_XML;
+ OS_PLATFORM_RESOURCES_FOLDER + AndroidConstants.FD_RES_VALUES + File.separator +
+ FN_ATTRS_XML;
/** Path of the attrs_manifest.xml file relative to a platform folder. */
public final static String OS_PLATFORM_ATTRS_MANIFEST_XML =
- OS_PLATFORM_RESOURCES_FOLDER + FD_VALUES + File.separator + FN_ATTRS_MANIFEST_XML;
+ OS_PLATFORM_RESOURCES_FOLDER + AndroidConstants.FD_RES_VALUES + File.separator +
+ FN_ATTRS_MANIFEST_XML;
/** Path of the layoutlib.jar file relative to a platform folder. */
public final static String OS_PLATFORM_LAYOUTLIB_JAR =
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
index 3e6e23e..d924d3d 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
@@ -18,11 +18,11 @@ package com.android.sdklib;
import com.android.annotations.VisibleForTesting;
import com.android.annotations.VisibleForTesting.Visibility;
+import com.android.io.FileWrapper;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.AndroidVersion.AndroidVersionException;
import com.android.sdklib.internal.project.ProjectProperties;
-import com.android.sdklib.io.FileWrapper;
import com.android.util.Pair;
import java.io.File;
@@ -233,13 +233,17 @@ public class SdkManager {
/**
* Loads the Platforms from the SDK.
+ * Creates the "platforms" folder if necessary.
+ *
* @param sdkOsPath Location of the SDK
* @param list the list to fill with the platforms.
* @param log the ISdkLog object receiving warning/error from the parsing. Cannot be null.
+ * @throws RuntimeException when the "platforms" folder is missing and cannot be created.
*/
private static void loadPlatforms(String sdkOsPath, ArrayList<IAndroidTarget> list,
ISdkLog log) {
File platformFolder = new File(sdkOsPath, SdkConstants.FD_PLATFORMS);
+
if (platformFolder.isDirectory()) {
File[] platforms = platformFolder.listFiles();
@@ -257,15 +261,18 @@ public class SdkManager {
return;
}
- String message = null;
- if (platformFolder.exists() == false) {
- message = "%s is missing.";
+ // Try to create it or complain if something else is in the way.
+ if (!platformFolder.exists()) {
+ if (!platformFolder.mkdir()) {
+ throw new RuntimeException(
+ String.format("Failed to create %1$s.",
+ platformFolder.getAbsolutePath()));
+ }
} else {
- message = "%s is not a folder.";
+ throw new RuntimeException(
+ String.format("%1$s is not a folder.",
+ platformFolder.getAbsolutePath()));
}
-
- throw new IllegalArgumentException(String.format(message,
- platformFolder.getAbsolutePath()));
}
/**
@@ -355,15 +362,17 @@ public class SdkManager {
return null;
}
+ String[] abiList = getAbiList(platformFolder.getAbsolutePath());
// create the target.
PlatformTarget target = new PlatformTarget(
sdkOsPath,
platformFolder.getAbsolutePath(),
- map,
apiNumber,
apiCodename,
apiName,
- revision);
+ revision,
+ abiList,
+ map);
// need to parse the skins.
String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS));
@@ -380,15 +389,43 @@ public class SdkManager {
return null;
}
+ /**
+ * Get all the abi types supported for a given target
+ * @param path Path where the images folder for a target is located
+ * @return an array of strings containing all the abi names for the target
+ */
+ private static String[] getAbiList(String path) {
+ ArrayList<String> list = new ArrayList<String>();
+
+ File imagesFolder = new File(path + File.separator + SdkConstants.OS_IMAGES_FOLDER);
+ File[] files = imagesFolder.listFiles();
+
+ if (files != null) {
+ // Loop through Images directory. If subdirectories exist, set multiprocessor mode
+ for (File file : files) {
+ if (file.isDirectory()) {
+ list.add(file.getName());
+ }
+ }
+ }
+ String[] abis = new String[list.size()];
+ list.toArray(abis);
+
+ return abis;
+ }
/**
* Loads the Add-on from the SDK.
+ * Creates the "add-ons" folder if necessary.
+ *
* @param osSdkPath Location of the SDK
* @param list the list to fill with the add-ons.
* @param log the ISdkLog object receiving warning/error from the parsing. Cannot be null.
+ * @throws RuntimeException when the "add-ons" folder is missing and cannot be created.
*/
private static void loadAddOns(String osSdkPath, ArrayList<IAndroidTarget> list, ISdkLog log) {
File addonFolder = new File(osSdkPath, SdkConstants.FD_ADDONS);
+
if (addonFolder.isDirectory()) {
File[] addons = addonFolder.listFiles();
@@ -407,15 +444,18 @@ public class SdkManager {
return;
}
- String message = null;
- if (addonFolder.exists() == false) {
- message = "%s is missing.";
+ // Try to create it or complain if something else is in the way.
+ if (!addonFolder.exists()) {
+ if (!addonFolder.mkdir()) {
+ throw new RuntimeException(
+ String.format("Failed to create %1$s.",
+ addonFolder.getAbsolutePath()));
+ }
} else {
- message = "%s is not a folder.";
+ throw new RuntimeException(
+ String.format("%1$s is not a folder.",
+ addonFolder.getAbsolutePath()));
}
-
- throw new IllegalArgumentException(String.format(message,
- addonFolder.getAbsolutePath()));
}
/**
@@ -514,8 +554,9 @@ public class SdkManager {
}
}
+ String[] abiList = getAbiList(addonDir.getAbsolutePath());
AddOnTarget target = new AddOnTarget(addonDir.getAbsolutePath(), name, vendor,
- revisionValue, description, libMap, baseTarget);
+ revisionValue, description, abiList, libMap, baseTarget);
// need to parse the skins.
String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS));
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java
new file mode 100755
index 0000000..81ffa5d
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java
@@ -0,0 +1,334 @@
+/*
+ * 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.android.sdklib.internal.avd;
+
+import com.android.prefs.AndroidLocation.AndroidLocationException;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * An immutable structure describing an Android Virtual Device.
+ */
+public final class AvdInfo implements Comparable<AvdInfo> {
+
+ /**
+ * Status for an {@link AvdInfo}. Indicates whether or not this AVD is valid.
+ */
+ public static enum AvdStatus {
+ /** No error */
+ OK,
+ /** Missing 'path' property in the ini file */
+ ERROR_PATH,
+ /** Missing config.ini file in the AVD data folder */
+ ERROR_CONFIG,
+ /** Missing 'target' property in the ini file */
+ ERROR_TARGET_HASH,
+ /** Target was not resolved from its hash */
+ ERROR_TARGET,
+ /** Unable to parse config.ini */
+ ERROR_PROPERTIES,
+ /** System Image folder in config.ini doesn't exist */
+ ERROR_IMAGE_DIR;
+ }
+
+ private final String mName;
+ private final File mIniFile;
+ private final String mFolderPath;
+ private final String mTargetHash;
+ private final IAndroidTarget mTarget;
+ private final String mAbiType;
+ private final Map<String, String> mProperties;
+ private final AvdStatus mStatus;
+
+ /**
+ * Creates a new valid AVD info. Values are immutable.
+ * <p/>
+ * Such an AVD is available and can be used.
+ * The error string is set to null.
+ *
+ * @param name The name of the AVD (for display or reference)
+ * @param iniFile The path to the config.ini file
+ * @param folderPath The path to the data directory
+ * @param targetHash the target hash
+ * @param target The target. Can be null, if the target was not resolved.
+ * @param abiType Name of the abi.
+ * @param properties The property map. Cannot be null.
+ */
+ public AvdInfo(String name,
+ File iniFile,
+ String folderPath,
+ String targetHash,
+ IAndroidTarget target,
+ String abiType,
+ Map<String, String> properties) {
+ this(name, iniFile, folderPath, targetHash, target, abiType, properties, AvdStatus.OK);
+ }
+
+ /**
+ * Creates a new <em>invalid</em> AVD info. Values are immutable.
+ * <p/>
+ * Such an AVD is not complete and cannot be used.
+ * The error string must be non-null.
+ *
+ * @param name The name of the AVD (for display or reference)
+ * @param iniFile The path to the config.ini file
+ * @param folderPath The path to the data directory
+ * @param targetHash the target hash
+ * @param target The target. Can be null, if the target was not resolved.
+ * @param abiType Name of the abi.
+ * @param properties The property map. Can be null.
+ * @param status The {@link AvdStatus} of this AVD. Cannot be null.
+ */
+ public AvdInfo(String name,
+ File iniFile,
+ String folderPath,
+ String targetHash,
+ IAndroidTarget target,
+ String abiType,
+ Map<String, String> properties,
+ AvdStatus status) {
+ mName = name;
+ mIniFile = iniFile;
+ mFolderPath = folderPath;
+ mTargetHash = targetHash;
+ mTarget = target;
+ mAbiType = abiType;
+ mProperties = properties == null ? null : Collections.unmodifiableMap(properties);
+ mStatus = status;
+ }
+
+ /** Returns the name of the AVD. */
+ public String getName() {
+ return mName;
+ }
+
+ /** Returns the path of the AVD data directory. */
+ public String getDataFolderPath() {
+ return mFolderPath;
+ }
+
+ /** Returns the processor type of the AVD. */
+ public String getAbiType() {
+ return mAbiType;
+ }
+
+ /** Convenience function to return a more user friendly name of the abi type. */
+ public static String getPrettyAbiType(String raw) {
+ String s = null;
+ if (raw.equalsIgnoreCase(SdkConstants.ABI_ARMEABI)) {
+ s = "ARM (" + SdkConstants.ABI_ARMEABI + ")";
+ }
+ else if (raw.equalsIgnoreCase(SdkConstants.ABI_INTEL_ATOM)) {
+ s = "Intel Atom (" + SdkConstants.ABI_INTEL_ATOM + ")";
+ }
+ else {
+ s = raw + " (" + raw + ")";
+ }
+ return s;
+ }
+
+ /**
+ * Returns the emulator executable path
+ * @param sdkPath path of the sdk
+ * @return path of the emulator executable
+ */
+ public String getEmulatorPath(String sdkPath) {
+ String path = sdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER;
+
+ // Start with base name of the emulator
+ path = path + SdkConstants.FN_EMULATOR;
+
+ // If not using ARM, add processor type to emulator command line
+ boolean useAbi = !getAbiType().equalsIgnoreCase(SdkConstants.ABI_ARMEABI);
+
+ if (useAbi) {
+ path = path + "-" + getAbiType(); //$NON-NLS-1$
+ }
+ // Add OS appropriate emulator extension (e.g., .exe on windows)
+ path = path + SdkConstants.FN_EMULATOR_EXTENSION;
+
+ // HACK: The AVD manager should look for "emulator" or for "emulator-abi" (if not arm).
+ // However this is a transition period and we don't have that unified "emulator" binary
+ // in AOSP so if we can't find the generic one, look for an abi-specific one with the
+ // special case that the armeabi one is actually named emulator-arm.
+ // TODO remove this kludge once no longer necessary.
+ if (!useAbi && !(new File(path).isFile())) {
+ path = sdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER;
+ path = path + SdkConstants.FN_EMULATOR;
+ path = path + "-" //$NON-NLS-1$
+ + getAbiType().replace(SdkConstants.ABI_ARMEABI, "arm"); //$NON-NLS-1$
+ path = path + SdkConstants.FN_EMULATOR_EXTENSION;
+ }
+
+ return path;
+ }
+
+ /**
+ * Returns the target hash string.
+ */
+ public String getTargetHash() {
+ return mTargetHash;
+ }
+
+ /** Returns the target of the AVD, or <code>null</code> if it has not been resolved. */
+ public IAndroidTarget getTarget() {
+ return mTarget;
+ }
+
+ /** Returns the {@link AvdStatus} of the receiver. */
+ public AvdStatus getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Helper method that returns the default AVD folder that would be used for a given
+ * AVD name <em>if and only if</em> the AVD was created with the default choice.
+ * <p/>
+ * Callers must NOT use this to "guess" the actual folder from an actual AVD since
+ * the purpose of the AVD .ini file is to be able to change this folder. Callers
+ * should however use this to create a new {@link AvdInfo} to setup its data folder
+ * to the default.
+ * <p/>
+ * The default is {@code getDefaultAvdFolder()/avdname.avd/}.
+ * <p/>
+ * For an actual existing AVD, callers must use {@link #getDataFolderPath()} instead.
+ *
+ * @param manager The AVD Manager, used to get the AVD storage path.
+ * @param avdName The name of the desired AVD.
+ * @throws AndroidLocationException if there's a problem getting android root directory.
+ */
+ public static File getDefaultAvdFolder(AvdManager manager, String avdName)
+ throws AndroidLocationException {
+ return new File(manager.getBaseAvdFolder(),
+ avdName + AvdManager.AVD_FOLDER_EXTENSION);
+ }
+
+ /**
+ * Helper method that returns the .ini {@link File} for a given AVD name.
+ * <p/>
+ * The default is {@code getDefaultAvdFolder()/avdname.ini}.
+ *
+ * @param manager The AVD Manager, used to get the AVD storage path.
+ * @param avdName The name of the desired AVD.
+ * @throws AndroidLocationException if there's a problem getting android root directory.
+ */
+ public static File getDefaultIniFile(AvdManager manager, String avdName)
+ throws AndroidLocationException {
+ String avdRoot = manager.getBaseAvdFolder();
+ return new File(avdRoot, avdName + AvdManager.INI_EXTENSION);
+ }
+
+ /**
+ * Returns the .ini {@link File} for this AVD.
+ */
+ public File getIniFile() {
+ return mIniFile;
+ }
+
+ /**
+ * Helper method that returns the Config {@link File} for a given AVD name.
+ */
+ public static File getConfigFile(String path) {
+ return new File(path, AvdManager.CONFIG_INI);
+ }
+
+ /**
+ * Returns the Config {@link File} for this AVD.
+ */
+ public File getConfigFile() {
+ return getConfigFile(mFolderPath);
+ }
+
+ /**
+ * Returns an unmodifiable map of properties for the AVD. This can be null.
+ */
+ public Map<String, String> getProperties() {
+ return mProperties;
+ }
+
+ /**
+ * Returns the error message for the AVD or <code>null</code> if {@link #getStatus()}
+ * returns {@link AvdStatus#OK}
+ */
+ public String getErrorMessage() {
+ switch (mStatus) {
+ case ERROR_PATH:
+ return String.format("Missing AVD 'path' property in %1$s", getIniFile());
+ case ERROR_CONFIG:
+ return String.format("Missing config.ini file in %1$s", mFolderPath);
+ case ERROR_TARGET_HASH:
+ return String.format("Missing 'target' property in %1$s", getIniFile());
+ case ERROR_TARGET:
+ return String.format("Unknown target '%1$s' in %2$s",
+ mTargetHash, getIniFile());
+ case ERROR_PROPERTIES:
+ return String.format("Failed to parse properties from %1$s",
+ getConfigFile());
+ case ERROR_IMAGE_DIR:
+ return String.format(
+ "Invalid value in image.sysdir. Run 'android update avd -n %1$s'",
+ mName);
+ case OK:
+ assert false;
+ return null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns whether an emulator is currently running the AVD.
+ */
+ public boolean isRunning() {
+ File f = new File(mFolderPath, "userdata-qemu.img.lock"); //$NON-NLS-1$
+ return f.isFile();
+ }
+
+ /**
+ * Compares this object with the specified object for order. Returns a
+ * negative integer, zero, or a positive integer as this object is less
+ * than, equal to, or greater than the specified object.
+ *
+ * @param o the Object to be compared.
+ * @return a negative integer, zero, or a positive integer as this object is
+ * less than, equal to, or greater than the specified object.
+ */
+ public int compareTo(AvdInfo o) {
+ // first handle possible missing targets (if the AVD failed to load for unresolved targets)
+ if (mTarget == null && o != null && o.mTarget == null) {
+ return 0;
+ } if (mTarget == null) {
+ return +1;
+ } else if (o == null || o.mTarget == null) {
+ return -1;
+ }
+
+ // then compare the targets
+ int targetDiff = mTarget.compareTo(o.mTarget);
+
+ if (targetDiff == 0) {
+ // same target? compare on the avd name
+ return mName.compareTo(o.mName);
+ }
+
+ return targetDiff;
+ }
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
index eba8e07..c9a3561 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
@@ -16,15 +16,16 @@
package com.android.sdklib.internal.avd;
+import com.android.io.FileWrapper;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus;
+import com.android.sdklib.internal.avd.AvdInfo.AvdStatus;
import com.android.sdklib.internal.project.ProjectProperties;
-import com.android.sdklib.io.FileWrapper;
+import com.android.util.Pair;
import java.io.BufferedReader;
import java.io.File;
@@ -37,7 +38,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
@@ -47,7 +47,7 @@ import java.util.regex.Pattern;
/**
* Android Virtual Device Manager to manage AVDs.
*/
-public final class AvdManager {
+public class AvdManager {
/**
* Exception thrown when something is wrong with a target path.
@@ -66,6 +66,13 @@ public final class AvdManager {
public final static String AVD_INFO_TARGET = "target"; //$NON-NLS-1$
/**
+ * AVD/config.ini key name representing the abi type of the specific avd
+ *
+ */
+ public final static String AVD_INI_ABI_TYPE = "abi.type"; //$NON-NLS-1$
+
+
+ /**
* AVD/config.ini key name representing the SDK-relative path of the skin folder, if any,
* or a 320x480 like constant for a numeric skin size.
*
@@ -92,6 +99,7 @@ public final class AvdManager {
* This property is for UI purposes only. It is not used by the emulator.
*
* @see #SDCARD_SIZE_PATTERN
+ * @see #parseSdcardSize(String, String[])
*/
public final static String AVD_INI_SDCARD_SIZE = "sdcard.size"; //$NON-NLS-1$
/**
@@ -123,11 +131,11 @@ public final class AvdManager {
public final static Pattern NUMERIC_SKIN_SIZE = Pattern.compile("([0-9]{2,})x([0-9]{2,})"); //$NON-NLS-1$
private final static String USERDATA_IMG = "userdata.img"; //$NON-NLS-1$
- private final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$
+ final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$
private final static String SDCARD_IMG = "sdcard.img"; //$NON-NLS-1$
private final static String SNAPSHOTS_IMG = "snapshots.img"; //$NON-NLS-1$
- private final static String INI_EXTENSION = ".ini"; //$NON-NLS-1$
+ final static String INI_EXTENSION = ".ini"; //$NON-NLS-1$
private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\" + //$NON-NLS-1$
INI_EXTENSION + "$", //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
@@ -137,8 +145,26 @@ public final class AvdManager {
/**
* Pattern for matching SD Card sizes, e.g. "4K" or "16M".
+ * Callers should use {@link #parseSdcardSize(String, String[])} instead of using this directly.
+ */
+ private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("(\\d+)([KMG])"); //$NON-NLS-1$
+
+ /**
+ * Minimal size of an SDCard image file in bytes. Currently 9 MiB.
+ */
+
+ public static final long SDCARD_MIN_BYTE_SIZE = 9<<20;
+ /**
+ * Maximal size of an SDCard image file in bytes. Currently 1023 GiB.
*/
- public final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("(\\d+)([MK])"); //$NON-NLS-1$
+ public static final long SDCARD_MAX_BYTE_SIZE = 1023L<<30;
+
+ /** The sdcard string represents a valid number but the size is outside of the allowed range. */
+ public final static int SDCARD_SIZE_NOT_IN_RANGE = 0;
+ /** The sdcard string looks like a size number+suffix but the number failed to decode. */
+ public final static int SDCARD_SIZE_INVALID = -1;
+ /** The sdcard string doesn't look like a size, it might be a path instead. */
+ public final static int SDCARD_NOT_SIZE_PATTERN = -2;
/** Regex used to validate characters that compose an AVD name. */
public final static Pattern RE_AVD_NAME = Pattern.compile("[a-zA-Z0-9._-]+"); //$NON-NLS-1$
@@ -148,212 +174,21 @@ public final class AvdManager {
public final static String HARDWARE_INI = "hardware.ini"; //$NON-NLS-1$
- /** An immutable structure describing an Android Virtual Device. */
- public static final class AvdInfo implements Comparable<AvdInfo> {
-
- /**
- * Status for an {@link AvdInfo}. Indicates whether or not this AVD is valid.
- */
- public static enum AvdStatus {
- /** No error */
- OK,
- /** Missing 'path' property in the ini file */
- ERROR_PATH,
- /** Missing config.ini file in the AVD data folder */
- ERROR_CONFIG,
- /** Missing 'target' property in the ini file */
- ERROR_TARGET_HASH,
- /** Target was not resolved from its hash */
- ERROR_TARGET,
- /** Unable to parse config.ini */
- ERROR_PROPERTIES,
- /** System Image folder in config.ini doesn't exist */
- ERROR_IMAGE_DIR;
- }
-
- private final String mName;
- private final String mPath;
- private final String mTargetHash;
- private final IAndroidTarget mTarget;
- private final Map<String, String> mProperties;
- private final AvdStatus mStatus;
-
- /**
- * Creates a new valid AVD info. Values are immutable.
- * <p/>
- * Such an AVD is available and can be used.
- * The error string is set to null.
- *
- * @param name The name of the AVD (for display or reference)
- * @param path The path to the config.ini file
- * @param targetHash the target hash
- * @param target The target. Can be null, if the target was not resolved.
- * @param properties The property map. Cannot be null.
- */
- public AvdInfo(String name, String path, String targetHash, IAndroidTarget target,
- Map<String, String> properties) {
- this(name, path, targetHash, target, properties, AvdStatus.OK);
- }
-
- /**
- * Creates a new <em>invalid</em> AVD info. Values are immutable.
- * <p/>
- * Such an AVD is not complete and cannot be used.
- * The error string must be non-null.
- *
- * @param name The name of the AVD (for display or reference)
- * @param path The path to the config.ini file
- * @param targetHash the target hash
- * @param target The target. Can be null, if the target was not resolved.
- * @param properties The property map. Can be null.
- * @param status The {@link AvdStatus} of this AVD. Cannot be null.
- */
- public AvdInfo(String name, String path, String targetHash, IAndroidTarget target,
- Map<String, String> properties, AvdStatus status) {
- mName = name;
- mPath = path;
- mTargetHash = targetHash;
- mTarget = target;
- mProperties = properties == null ? null : Collections.unmodifiableMap(properties);
- mStatus = status;
- }
-
- /** Returns the name of the AVD. */
- public String getName() {
- return mName;
- }
-
- /** Returns the path of the AVD data directory. */
- public String getPath() {
- return mPath;
- }
-
- /**
- * Returns the target hash string.
- */
- public String getTargetHash() {
- return mTargetHash;
- }
-
- /** Returns the target of the AVD, or <code>null</code> if it has not been resolved. */
- public IAndroidTarget getTarget() {
- return mTarget;
- }
-
- /** Returns the {@link AvdStatus} of the receiver. */
- public AvdStatus getStatus() {
- return mStatus;
- }
-
- /**
- * Helper method that returns the .ini {@link File} for a given AVD name.
- * @throws AndroidLocationException if there's a problem getting android root directory.
- */
- public static File getIniFile(String name) throws AndroidLocationException {
- String avdRoot;
- avdRoot = getBaseAvdFolder();
- return new File(avdRoot, name + INI_EXTENSION);
- }
-
- /**
- * Returns the .ini {@link File} for this AVD.
- * @throws AndroidLocationException if there's a problem getting android root directory.
- */
- public File getIniFile() throws AndroidLocationException {
- return getIniFile(mName);
- }
-
- /**
- * Helper method that returns the Config {@link File} for a given AVD name.
- */
- public static File getConfigFile(String path) {
- return new File(path, CONFIG_INI);
- }
-
- /**
- * Returns the Config {@link File} for this AVD.
- */
- public File getConfigFile() {
- return getConfigFile(mPath);
- }
-
- /**
- * Returns an unmodifiable map of properties for the AVD. This can be null.
- */
- public Map<String, String> getProperties() {
- return mProperties;
- }
-
- /**
- * Returns the error message for the AVD or <code>null</code> if {@link #getStatus()}
- * returns {@link AvdStatus#OK}
- */
- public String getErrorMessage() {
- try {
- switch (mStatus) {
- case ERROR_PATH:
- return String.format("Missing AVD 'path' property in %1$s", getIniFile());
- case ERROR_CONFIG:
- return String.format("Missing config.ini file in %1$s", mPath);
- case ERROR_TARGET_HASH:
- return String.format("Missing 'target' property in %1$s", getIniFile());
- case ERROR_TARGET:
- return String.format("Unknown target '%1$s' in %2$s",
- mTargetHash, getIniFile());
- case ERROR_PROPERTIES:
- return String.format("Failed to parse properties from %1$s",
- getConfigFile());
- case ERROR_IMAGE_DIR:
- return String.format(
- "Invalid value in image.sysdir. Run 'android update avd -n %1$s'",
- mName);
- case OK:
- assert false;
- return null;
- }
- } catch (AndroidLocationException e) {
- return "Unable to get HOME folder.";
- }
-
- return null;
- }
-
- /**
- * Returns whether an emulator is currently running the AVD.
- */
- public boolean isRunning() {
- File f = new File(mPath, "userdata-qemu.img.lock");
- return f.isFile();
- }
-
+ /**
+ * Status returned by {@link AvdManager#isAvdNameConflicting(String)}.
+ */
+ public static enum AvdConflict {
+ /** There is no known conflict for the given AVD name. */
+ NO_CONFLICT,
+ /** The AVD name conflicts with an existing valid AVD. */
+ CONFLICT_EXISTING_AVD,
+ /** The AVD name conflicts with an existing invalid AVD. */
+ CONFLICT_INVALID_AVD,
/**
- * Compares this object with the specified object for order. Returns a
- * negative integer, zero, or a positive integer as this object is less
- * than, equal to, or greater than the specified object.
- *
- * @param o the Object to be compared.
- * @return a negative integer, zero, or a positive integer as this object is
- * less than, equal to, or greater than the specified object.
+ * The AVD name does not conflict with any known AVD however there are
+ * files or directory that would cause a conflict if this were to be created.
*/
- public int compareTo(AvdInfo o) {
- // first handle possible missing targets (if the AVD failed to load for
- // unresolved targets.
- if (mTarget == null) {
- return +1;
- } else if (o.mTarget == null) {
- return -1;
- }
-
- // then compare the targets
- int targetDiff = mTarget.compareTo(o.mTarget);
-
- if (targetDiff == 0) {
- // same target? compare on the avd name
- return mName.compareTo(o.mName);
- }
-
- return targetDiff;
- }
+ CONFLICT_EXISTING_PATH,
}
private final ArrayList<AvdInfo> mAllAvdList = new ArrayList<AvdInfo>();
@@ -362,15 +197,6 @@ public final class AvdManager {
private final SdkManager mSdkManager;
/**
- * Returns the base folder where AVDs are created.
- *
- * @throws AndroidLocationException
- */
- public static String getBaseAvdFolder() throws AndroidLocationException {
- return AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
- }
-
- /**
* Creates an AVD Manager for a given SDK represented by a {@link SdkManager}.
* @param sdkManager The SDK.
* @param log The log object to receive the log of the initial loading of the AVDs.
@@ -386,6 +212,16 @@ public final class AvdManager {
}
/**
+ * Returns the base folder where AVDs are created.
+ *
+ * @throws AndroidLocationException
+ */
+ public String getBaseAvdFolder() throws AndroidLocationException {
+ assert AndroidLocation.getFolder().endsWith(File.separator);
+ return AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
+ }
+
+ /**
* Returns the {@link SdkManager} associated with the {@link AvdManager}.
*/
public SdkManager getSdkManager() {
@@ -393,6 +229,71 @@ public final class AvdManager {
}
/**
+ * Parse the sdcard string to decode the size.
+ * Returns:
+ * <ul>
+ * <li> The size in bytes > 0 if the sdcard string is a valid size in the allowed range.
+ * <li> {@link #SDCARD_SIZE_NOT_IN_RANGE} (0)
+ * if the sdcard string is a valid size NOT in the allowed range.
+ * <li> {@link #SDCARD_SIZE_INVALID} (-1)
+ * if the sdcard string is number that fails to parse correctly.
+ * <li> {@link #SDCARD_NOT_SIZE_PATTERN} (-2)
+ * if the sdcard string is not a number, in which case it's probably a file path.
+ * </ul>
+ *
+ * @param sdcard The sdcard string, which can be a file path, a size string or something else.
+ * @param parsedStrings If non-null, an array of 2 strings. The first string will be
+ * filled with the parsed numeric size and the second one will be filled with the
+ * parsed suffix. This is filled even if the returned size is deemed out of range or
+ * failed to parse. The values are null if the sdcard is not a size pattern.
+ * @return A size in byte if > 0, or {@link #SDCARD_SIZE_NOT_IN_RANGE},
+ * {@link #SDCARD_SIZE_INVALID} or {@link #SDCARD_NOT_SIZE_PATTERN} as error codes.
+ */
+ public static long parseSdcardSize(String sdcard, String[] parsedStrings) {
+
+ if (parsedStrings != null) {
+ assert parsedStrings.length == 2;
+ parsedStrings[0] = null;
+ parsedStrings[1] = null;
+ }
+
+ Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard);
+ if (m.matches()) {
+ if (parsedStrings != null) {
+ assert parsedStrings.length == 2;
+ parsedStrings[0] = m.group(1);
+ parsedStrings[1] = m.group(2);
+ }
+
+ // get the sdcard values for checks
+ try {
+ long sdcardSize = Long.parseLong(m.group(1));
+
+ String sdcardSizeModifier = m.group(2);
+ if ("K".equals(sdcardSizeModifier)) { //$NON-NLS-1$
+ sdcardSize <<= 10;
+ } else if ("M".equals(sdcardSizeModifier)) { //$NON-NLS-1$
+ sdcardSize <<= 20;
+ } else if ("G".equals(sdcardSizeModifier)) { //$NON-NLS-1$
+ sdcardSize <<= 30;
+ }
+
+ if (sdcardSize < SDCARD_MIN_BYTE_SIZE ||
+ sdcardSize > SDCARD_MAX_BYTE_SIZE) {
+ return SDCARD_SIZE_NOT_IN_RANGE;
+ }
+
+ return sdcardSize;
+ } catch (NumberFormatException e) {
+ // This could happen if the number is too large to fit in a long.
+ return SDCARD_SIZE_INVALID;
+ }
+ }
+
+ return SDCARD_NOT_SIZE_PATTERN;
+ }
+
+ /**
* Returns all the existing AVDs.
* @return a newly allocated array containing all the AVDs.
*/
@@ -476,6 +377,53 @@ public final class AvdManager {
}
/**
+ * Returns whether this AVD name would generate a conflict.
+ *
+ * @param name the name of the AVD to return
+ * @return A pair of {@link AvdConflict} and the path or AVD name that conflicts.
+ */
+ public Pair<AvdConflict, String> isAvdNameConflicting(String name) {
+
+ boolean ignoreCase = SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS;
+
+ // Check whether we have a conflict with an existing or invalid AVD
+ // known to the manager.
+ synchronized (mAllAvdList) {
+ for (AvdInfo info : mAllAvdList) {
+ String name2 = info.getName();
+ if (name2.equals(name) || (ignoreCase && name2.equalsIgnoreCase(name))) {
+ if (info.getStatus() == AvdStatus.OK) {
+ return Pair.of(AvdConflict.CONFLICT_EXISTING_AVD, name2);
+ } else {
+ return Pair.of(AvdConflict.CONFLICT_INVALID_AVD, name2);
+ }
+ }
+ }
+ }
+
+ // No conflict with known AVDs.
+ // Are some existing files/folders in the way of creating this AVD?
+
+ try {
+ File file = AvdInfo.getDefaultIniFile(this, name);
+ if (file.exists()) {
+ return Pair.of(AvdConflict.CONFLICT_EXISTING_PATH, file.getPath());
+ }
+
+ file = AvdInfo.getDefaultAvdFolder(this, name);
+ if (file.exists()) {
+ return Pair.of(AvdConflict.CONFLICT_EXISTING_PATH, file.getPath());
+ }
+
+ } catch (AndroidLocationException e) {
+ // ignore
+ }
+
+
+ return Pair.of(AvdConflict.NO_CONFLICT, null);
+ }
+
+ /**
* Reloads the AVD list.
* @param log the log object to receive action logs. Cannot be null.
* @throws AndroidLocationException if there was an error finding the location of the
@@ -495,37 +443,38 @@ public final class AvdManager {
}
/**
- * Creates a new AVD, but with no snapshot.
- *
- * See {@link #createAvd(File, String, IAndroidTarget, String, String, Map, boolean, boolean, ISdkLog)}
- **/
- @Deprecated
- public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target, String skinName,
- String sdcard, Map<String, String> hardwareConfig, boolean removePrevious,
- ISdkLog log) {
- return createAvd(avdFolder, name, target, skinName, sdcard, hardwareConfig, removePrevious,
- false, log);
- }
-
- /**
* Creates a new AVD. It is expected that there is no existing AVD with this name already.
*
* @param avdFolder the data folder for the AVD. It will be created as needed.
- * @param name the name of the AVD
+ * Unless you want to locate it in a specific directory, the ideal default is
+ * {@code AvdManager.AvdInfo.getAvdFolder}.
+ * @param avdName the name of the AVD
* @param target the target of the AVD
+ * @param abiType the abi type of the AVD
* @param skinName the name of the skin. Can be null. Must have been verified by caller.
* @param sdcard the parameter value for the sdCard. Can be null. This is either a path to
* an existing sdcard image or a sdcard size (\d+, \d+K, \dM).
* @param hardwareConfig the hardware setup for the AVD. Can be null to use defaults.
- * @param removePrevious If true remove any previous files.
* @param createSnapshot If true copy a blank snapshot image into the AVD.
+ * @param removePrevious If true remove any previous files.
+ * @param editExisting If true, edit an existing AVD, changing only the minimum required.
+ * This won't remove files unless required or unless {@code removePrevious} is set.
* @param log the log object to receive action logs. Cannot be null.
* @return The new {@link AvdInfo} in case of success (which has just been added to the
* internal list) or null in case of failure.
*/
- public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target,
- String skinName, String sdcard, Map<String,String> hardwareConfig,
- boolean removePrevious, boolean createSnapshot, ISdkLog log) {
+ public AvdInfo createAvd(
+ File avdFolder,
+ String avdName,
+ IAndroidTarget target,
+ String abiType,
+ String skinName,
+ String sdcard,
+ Map<String,String> hardwareConfig,
+ boolean createSnapshot,
+ boolean removePrevious,
+ boolean editExisting,
+ ISdkLog log) {
if (log == null) {
throw new IllegalArgumentException("log cannot be null");
}
@@ -542,8 +491,9 @@ public final class AvdManager {
} catch (SecurityException e) {
log.error(e, "Failed to delete %1$s", avdFolder.getAbsolutePath());
}
- } else {
- // AVD shouldn't already exist if removePrevious is false.
+ } else if (!editExisting) {
+ // AVD shouldn't already exist if removePrevious is false and
+ // we're not editing an existing AVD.
log.error(null,
"Folder %1$s is in the way. Use --force if you want to overwrite.",
avdFolder.getAbsolutePath());
@@ -552,23 +502,28 @@ public final class AvdManager {
} else {
// create the AVD folder.
avdFolder.mkdir();
+ // We're not editing an existing AVD.
+ editExisting = false;
}
// actually write the ini file
- iniFile = createAvdIniFile(name, avdFolder, target);
+ iniFile = createAvdIniFile(avdName, avdFolder, target, removePrevious);
// writes the userdata.img in it.
- String imagePath = target.getPath(IAndroidTarget.IMAGES);
+ String imagePath = target.getImagePath(abiType);
+
File userdataSrc = new File(imagePath, USERDATA_IMG);
if (userdataSrc.exists() == false && target.isPlatform() == false) {
- imagePath = target.getParent().getPath(IAndroidTarget.IMAGES);
+ imagePath =
+ target.getParent().getImagePath(abiType);
userdataSrc = new File(imagePath, USERDATA_IMG);
}
if (userdataSrc.exists() == false) {
- log.error(null, "Unable to find a '%1$s' file to copy into the AVD folder.",
- USERDATA_IMG);
+ log.error(null,
+ "Unable to find a '%1$s' file of '%2$s' to copy into the AVD folder.",
+ USERDATA_IMG, imagePath);
needCleanup = true;
return null;
}
@@ -576,30 +531,48 @@ public final class AvdManager {
copyImageFile(userdataSrc, userdataDest);
+ if (userdataDest.exists() == false) {
+ log.error(null, "Unable to create '%1$s' file in the AVD folder.",
+ userdataDest);
+ needCleanup = true;
+ return null;
+ }
+
// Config file.
HashMap<String, String> values = new HashMap<String, String>();
- if (setImagePathProperties(target, values, log) == false) {
- needCleanup = true;
- return null;
+ if (setImagePathProperties(target, abiType, values, log) == false) {
+ log.error(null, "Failed to set image path properties in the AVD folder.");
+ needCleanup = true;
+ return null;
}
// Create the snapshot file
if (createSnapshot) {
- String toolsLib = mSdkManager.getLocation() + File.separator
- + SdkConstants.OS_SDK_TOOLS_LIB_EMULATOR_FOLDER;
- File snapshotBlank = new File(toolsLib, SNAPSHOTS_IMG);
- if (snapshotBlank.exists() == false) {
- log.error(null, "Unable to find a '%2$s%1$s' file to copy into the AVD folder.",
- SNAPSHOTS_IMG, toolsLib);
- needCleanup = true;
- return null;
- }
File snapshotDest = new File(avdFolder, SNAPSHOTS_IMG);
- copyImageFile(snapshotBlank, snapshotDest);
+ if (snapshotDest.isFile() && editExisting) {
+ log.printf("Snapshot image already present, was not changed.");
+
+ } else {
+ String toolsLib = mSdkManager.getLocation() + File.separator
+ + SdkConstants.OS_SDK_TOOLS_LIB_EMULATOR_FOLDER;
+ File snapshotBlank = new File(toolsLib, SNAPSHOTS_IMG);
+ if (snapshotBlank.exists() == false) {
+ log.error(null,
+ "Unable to find a '%2$s%1$s' file to copy into the AVD folder.",
+ SNAPSHOTS_IMG, toolsLib);
+ needCleanup = true;
+ return null;
+ }
+
+ copyImageFile(snapshotBlank, snapshotDest);
+ }
values.put(AVD_INI_SNAPSHOT_PRESENT, "true");
}
+ // Now the abi type
+ values.put(AVD_INI_ABI_TYPE, abiType);
+
// Now the skin.
if (skinName == null || skinName.length() == 0) {
skinName = target.getDefaultSkin();
@@ -616,6 +589,7 @@ public final class AvdManager {
// assume skin name is valid
String skinPath = getSkinRelativePath(skinName, target, log);
if (skinPath == null) {
+ log.error(null, "Missing skinpath in the AVD folder.");
needCleanup = true;
return null;
}
@@ -625,54 +599,48 @@ public final class AvdManager {
}
if (sdcard != null && sdcard.length() > 0) {
- File sdcardFile = new File(sdcard);
- if (sdcardFile.isFile()) {
- // sdcard value is an external sdcard, so we put its path into the config.ini
- values.put(AVD_INI_SDCARD_PATH, sdcard);
+ // Sdcard is possibly a size. In that case we create a file called 'sdcard.img'
+ // in the AVD folder, and do not put any value in config.ini.
+
+ long sdcardSize = parseSdcardSize(sdcard, null/*parsedStrings*/);
+
+ if (sdcardSize == SDCARD_SIZE_NOT_IN_RANGE) {
+ log.error(null, "SD Card size must be in the range 9 MiB..1023 GiB.");
+ needCleanup = true;
+ return null;
+
+ } else if (sdcardSize == SDCARD_SIZE_INVALID) {
+ log.error(null, "Unable to parse SD Card size");
+ needCleanup = true;
+ return null;
+
+ } else if (sdcardSize == SDCARD_NOT_SIZE_PATTERN) {
+ File sdcardFile = new File(sdcard);
+ if (sdcardFile.isFile()) {
+ // sdcard value is an external sdcard, so we put its path into the config.ini
+ values.put(AVD_INI_SDCARD_PATH, sdcard);
+ } else {
+ log.error(null, "'%1$s' is not recognized as a valid sdcard value.\n"
+ + "Value should be:\n" + "1. path to an sdcard.\n"
+ + "2. size of the sdcard to create: <size>[K|M]", sdcard);
+ needCleanup = true;
+ return null;
+ }
} else {
- // Sdcard is possibly a size. In that case we create a file called 'sdcard.img'
- // in the AVD folder, and do not put any value in config.ini.
-
- // First, check that it matches the pattern for sdcard size
- Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard);
- if (m.matches()) {
- // get the sdcard values for checks
- try {
- long sdcardSize = Long.parseLong(m.group(1));
-
- // prevent overflow: no more than 999GB
- // 10 digit for MiB, 13 for KiB
- int digitCount = m.group(1).length();
-
- String sdcardSizeModifier = m.group(2);
- if ("K".equals(sdcardSizeModifier)) {
- sdcardSize *= 1024L;
- } else { // must be "M" per the pattern
- sdcardSize *= 1024L * 1024L;
- digitCount += 3; // convert the number of digit into "KiB"
- }
-
- if (digitCount >= 13) {
- log.error(null, "SD Card size is too big!");
- needCleanup = true;
- return null;
- }
-
- if (sdcardSize < 9 * 1024 * 1024) {
- log.error(null, "SD Card size must be at least 9MB");
- needCleanup = true;
- return null;
- }
- } catch (NumberFormatException e) {
- // this should never happen since the string is validated
- // by the regexp
- log.error(null, "Unable to parse SD Card size");
- needCleanup = true;
- return null;
+ // create the sdcard.
+ File sdcardFile = new File(avdFolder, SDCARD_IMG);
+
+ boolean runMkSdcard = true;
+ if (sdcardFile.exists()) {
+ if (sdcardFile.length() == sdcardSize && editExisting) {
+ // There's already an sdcard file with the right size and we're
+ // not overriding it... so don't remove it.
+ runMkSdcard = false;
+ log.printf("SD Card already present with same size, was not changed.");
}
+ }
- // create the sdcard.
- sdcardFile = new File(avdFolder, SDCARD_IMG);
+ if (runMkSdcard) {
String path = sdcardFile.getAbsolutePath();
// execute mksdcard with the proper parameters.
@@ -688,21 +656,16 @@ public final class AvdManager {
}
if (createSdCard(mkSdCard.getAbsolutePath(), sdcard, path, log) == false) {
+ log.error(null, "Failed to create sdcard in the AVD folder.");
needCleanup = true;
return null; // mksdcard output has already been displayed, no need to
// output anything else.
}
-
- // add a property containing the size of the sdcard for display purpose
- // only when the dev does 'android list avd'
- values.put(AVD_INI_SDCARD_SIZE, sdcard);
- } else {
- log.error(null, "'%1$s' is not recognized as a valid sdcard value.\n"
- + "Value should be:\n" + "1. path to an sdcard.\n"
- + "2. size of the sdcard to create: <size>[K|M]", sdcard);
- needCleanup = true;
- return null;
}
+
+ // add a property containing the size of the sdcard for display purpose
+ // only when the dev does 'android list avd'
+ values.put(AVD_INI_SDCARD_SIZE, sdcard);
}
}
@@ -757,12 +720,23 @@ public final class AvdManager {
StringBuilder report = new StringBuilder();
if (target.isPlatform()) {
- report.append(String.format("Created AVD '%1$s' based on %2$s",
- name, target.getName()));
+ if (editExisting) {
+ report.append(String.format("Updated AVD '%1$s' based on %2$s",
+ avdName, target.getName()));
+ } else {
+ report.append(String.format("Created AVD '%1$s' based on %2$s",
+ avdName, target.getName()));
+ }
} else {
- report.append(String.format("Created AVD '%1$s' based on %2$s (%3$s)", name,
- target.getName(), target.getVendor()));
+ if (editExisting) {
+ report.append(String.format("Updated AVD '%1$s' based on %2$s (%3$s)", avdName,
+ target.getName(), target.getVendor()));
+ } else {
+ report.append(String.format("Created AVD '%1$s' based on %2$s (%3$s)", avdName,
+ target.getName(), target.getVendor()));
+ }
}
+ report.append(String.format(", %s processor", AvdInfo.getPrettyAbiType(abiType)));
// display the chosen hardware config
if (finalHardwareValues.size() > 0) {
@@ -777,28 +751,31 @@ public final class AvdManager {
log.printf(report.toString());
// create the AvdInfo object, and add it to the list
- AvdInfo newAvdInfo = new AvdInfo(name,
+ AvdInfo newAvdInfo = new AvdInfo(
+ avdName,
+ iniFile,
avdFolder.getAbsolutePath(),
target.hashString(),
- target, values);
+ target, abiType, values);
- AvdInfo oldAvdInfo = getAvd(name, false /*validAvdOnly*/);
+ AvdInfo oldAvdInfo = getAvd(avdName, false /*validAvdOnly*/);
synchronized (mAllAvdList) {
- if (oldAvdInfo != null && removePrevious) {
+ if (oldAvdInfo != null && (removePrevious || editExisting)) {
mAllAvdList.remove(oldAvdInfo);
}
mAllAvdList.add(newAvdInfo);
mValidAvdList = mBrokenAvdList = null;
}
- if (removePrevious &&
+ if ((removePrevious || editExisting) &&
newAvdInfo != null &&
oldAvdInfo != null &&
- !oldAvdInfo.getPath().equals(newAvdInfo.getPath())) {
- log.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath());
+ !oldAvdInfo.getDataFolderPath().equals(newAvdInfo.getDataFolderPath())) {
+ log.warning("Removing previous AVD directory at %s",
+ oldAvdInfo.getDataFolderPath());
// Remove the old data directory
- File dir = new File(oldAvdInfo.getPath());
+ File dir = new File(oldAvdInfo.getDataFolderPath());
try {
deleteContentOf(dir);
dir.delete();
@@ -859,9 +836,9 @@ public final class AvdManager {
* is not empty. If the image folder is empty or does not exist, <code>null</code> is returned.
* @throws InvalidTargetPathException if the target image folder is not in the current SDK.
*/
- private String getImageRelativePath(IAndroidTarget target)
+ private String getImageRelativePath(IAndroidTarget target, String abiType)
throws InvalidTargetPathException {
- String imageFullPath = target.getPath(IAndroidTarget.IMAGES);
+ String imageFullPath = target.getImagePath(abiType);
// make this path relative to the SDK location
String sdkLocation = mSdkManager.getLocation();
@@ -957,13 +934,27 @@ public final class AvdManager {
* @param name of the AVD.
* @param avdFolder path for the data folder of the AVD.
* @param target of the AVD.
+ * @param removePrevious True if an existing ini file should be removed.
* @throws AndroidLocationException if there's a problem getting android root directory.
* @throws IOException if {@link File#getAbsolutePath()} fails.
*/
- private File createAvdIniFile(String name, File avdFolder, IAndroidTarget target)
+ private File createAvdIniFile(String name,
+ File avdFolder,
+ IAndroidTarget target,
+ boolean removePrevious)
throws AndroidLocationException, IOException {
+ File iniFile = AvdInfo.getDefaultIniFile(this, name);
+
+ if (removePrevious) {
+ if (iniFile.isFile()) {
+ iniFile.delete();
+ } else if (iniFile.isDirectory()) {
+ deleteContentOf(iniFile);
+ iniFile.delete();
+ }
+ }
+
HashMap<String, String> values = new HashMap<String, String>();
- File iniFile = AvdInfo.getIniFile(name);
values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath());
values.put(AVD_INFO_TARGET, target.hashString());
writeIniFile(iniFile, values);
@@ -979,7 +970,10 @@ public final class AvdManager {
* @throws IOException if {@link File#getAbsolutePath()} fails.
*/
private File createAvdIniFile(AvdInfo info) throws AndroidLocationException, IOException {
- return createAvdIniFile(info.getName(), new File(info.getPath()), info.getTarget());
+ return createAvdIniFile(info.getName(),
+ new File(info.getDataFolderPath()),
+ info.getTarget(),
+ false /*removePrevious*/);
}
/**
@@ -1010,7 +1004,7 @@ public final class AvdManager {
}
}
- String path = avdInfo.getPath();
+ String path = avdInfo.getDataFolderPath();
if (path != null) {
f = new File(path);
if (f.exists()) {
@@ -1032,8 +1026,6 @@ public final class AvdManager {
return true;
}
- } catch (AndroidLocationException e) {
- log.error(e, null);
} catch (IOException e) {
log.error(e, null);
} catch (SecurityException e) {
@@ -1060,17 +1052,25 @@ public final class AvdManager {
try {
if (paramFolderPath != null) {
- File f = new File(avdInfo.getPath());
- log.warning("Moving '%1$s' to '%2$s'.", avdInfo.getPath(), paramFolderPath);
+ File f = new File(avdInfo.getDataFolderPath());
+ log.warning("Moving '%1$s' to '%2$s'.",
+ avdInfo.getDataFolderPath(),
+ paramFolderPath);
if (!f.renameTo(new File(paramFolderPath))) {
log.error(null, "Failed to move '%1$s' to '%2$s'.",
- avdInfo.getPath(), paramFolderPath);
+ avdInfo.getDataFolderPath(), paramFolderPath);
return false;
}
// update AVD info
- AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath,
- avdInfo.getTargetHash(), avdInfo.getTarget(), avdInfo.getProperties());
+ AvdInfo info = new AvdInfo(
+ avdInfo.getName(),
+ avdInfo.getIniFile(),
+ paramFolderPath,
+ avdInfo.getTargetHash(),
+ avdInfo.getTarget(),
+ avdInfo.getAbiType(),
+ avdInfo.getProperties());
replaceAvd(avdInfo, info);
// update the ini file
@@ -1079,7 +1079,7 @@ public final class AvdManager {
if (newName != null) {
File oldIniFile = avdInfo.getIniFile();
- File newIniFile = AvdInfo.getIniFile(newName);
+ File newIniFile = AvdInfo.getDefaultIniFile(this, newName);
log.warning("Moving '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath());
if (!oldIniFile.renameTo(newIniFile)) {
@@ -1089,8 +1089,14 @@ public final class AvdManager {
}
// update AVD info
- AvdInfo info = new AvdInfo(newName, avdInfo.getPath(),
- avdInfo.getTargetHash(), avdInfo.getTarget(), avdInfo.getProperties());
+ AvdInfo info = new AvdInfo(
+ newName,
+ avdInfo.getIniFile(),
+ avdInfo.getDataFolderPath(),
+ avdInfo.getTargetHash(),
+ avdInfo.getTarget(),
+ avdInfo.getAbiType(),
+ avdInfo.getProperties());
replaceAvd(avdInfo, info);
}
@@ -1135,19 +1141,20 @@ public final class AvdManager {
* <p/>
* This lists the $HOME/.android/avd/<name>.ini files.
* Such files are properties file than then indicate where the AVD folder is located.
+ * <p/>
+ * Note: the method is to be considered private. It is made protected so that
+ * unit tests can easily override the AVD root.
*
* @return A new {@link File} array or null. The array might be empty.
* @throws AndroidLocationException if there's a problem getting android root directory.
*/
private File[] buildAvdFilesList() throws AndroidLocationException {
- // get the Android prefs location.
- String avdRoot = AvdManager.getBaseAvdFolder();
+ File folder = new File(getBaseAvdFolder());
// ensure folder validity.
- File folder = new File(avdRoot);
if (folder.isFile()) {
throw new AndroidLocationException(
- String.format("%1$s is not a valid folder.", avdRoot));
+ String.format("%1$s is not a valid folder.", folder.getAbsolutePath()));
} else if (folder.exists() == false) {
// folder is not there, we create it and return
folder.mkdirs();
@@ -1192,14 +1199,14 @@ public final class AvdManager {
/**
* Parses an AVD .ini file to create an {@link AvdInfo}.
*
- * @param path The path to the AVD .ini file
+ * @param iniPath The path to the AVD .ini file
* @param log the log object to receive action logs. Cannot be null.
* @return A new {@link AvdInfo} with an {@link AvdStatus} indicating whether this AVD is
* valid or not.
*/
- private AvdInfo parseAvdInfo(File path, ISdkLog log) {
+ private AvdInfo parseAvdInfo(File iniPath, ISdkLog log) {
Map<String, String> map = ProjectProperties.parsePropertyFile(
- new FileWrapper(path),
+ new FileWrapper(iniPath),
log);
String avdPath = map.get(AVD_INFO_PATH);
@@ -1227,12 +1234,20 @@ public final class AvdManager {
}
// get name
- String name = path.getName();
- Matcher matcher = INI_NAME_PATTERN.matcher(path.getName());
+ String name = iniPath.getName();
+ Matcher matcher = INI_NAME_PATTERN.matcher(iniPath.getName());
if (matcher.matches()) {
name = matcher.group(1);
}
+ // get abi type
+ String abiType = properties == null ? null : properties.get(AVD_INI_ABI_TYPE);
+ // for the avds created previously without enhancement, i.e. They are created based
+ // on previous API Levels. They are supposed to have ARM processor type
+ if (abiType == null) {
+ abiType = SdkConstants.ABI_ARMEABI;
+ }
+
// check the image.sysdir are valid
boolean validImageSysdir = true;
if (properties != null) {
@@ -1275,9 +1290,11 @@ public final class AvdManager {
AvdInfo info = new AvdInfo(
name,
+ iniPath,
avdPath,
targetHash,
target,
+ abiType,
properties,
status);
@@ -1489,7 +1506,7 @@ public final class AvdManager {
AvdStatus status;
// create the path to the new system images.
- if (setImagePathProperties(avd.getTarget(), properties, log)) {
+ if (setImagePathProperties(avd.getTarget(), avd.getAbiType(), properties, log)) {
if (properties.containsKey(AVD_INI_IMAGES_1)) {
log.printf("Updated '%1$s' with value '%2$s'\n", AVD_INI_IMAGES_1,
properties.get(AVD_INI_IMAGES_1));
@@ -1509,7 +1526,7 @@ public final class AvdManager {
}
// now write the config file
- File configIniFile = new File(avd.getPath(), CONFIG_INI);
+ File configIniFile = new File(avd.getDataFolderPath(), CONFIG_INI);
writeIniFile(configIniFile, properties);
// finally create a new AvdInfo for this unbroken avd and add it to the list.
@@ -1518,9 +1535,11 @@ public final class AvdManager {
// FIXME: We may want to create this AvdInfo by reparsing the AVD instead. This could detect other errors.
AvdInfo newAvd = new AvdInfo(
avd.getName(),
- avd.getPath(),
+ avd.getIniFile(),
+ avd.getDataFolderPath(),
avd.getTargetHash(),
avd.getTarget(),
+ avd.getAbiType(),
properties,
status);
@@ -1530,13 +1549,14 @@ public final class AvdManager {
/**
* Sets the paths to the system images in a properties map.
* @param target the target in which to find the system images.
+ * @param abiType the abi type of the avd to find
+ * the architecture-dependent system images.
* @param properties the properties in which to set the paths.
* @param log the log object to receive action logs. Cannot be null.
* @return true if success, false if some path are missing.
*/
private boolean setImagePathProperties(IAndroidTarget target,
- Map<String, String> properties,
- ISdkLog log) {
+ String abiType, Map<String, String> properties, ISdkLog log) {
properties.remove(AVD_INI_IMAGES_1);
properties.remove(AVD_INI_IMAGES_2);
@@ -1544,7 +1564,7 @@ public final class AvdManager {
String property = AVD_INI_IMAGES_1;
// First the image folders of the target itself
- String imagePath = getImageRelativePath(target);
+ String imagePath = getImageRelativePath(target, abiType);
if (imagePath != null) {
properties.put(property, imagePath);
property = AVD_INI_IMAGES_2;
@@ -1554,7 +1574,7 @@ public final class AvdManager {
// If the target is an add-on we need to add the Platform image as a backup.
IAndroidTarget parent = target.getParent();
if (parent != null) {
- imagePath = getImageRelativePath(parent);
+ imagePath = getImageRelativePath(parent, abiType);
if (imagePath != null) {
properties.put(property, imagePath);
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java
index bef7ccf..d31414c 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java
@@ -36,49 +36,49 @@ import java.security.cert.CertificateException;
* <p/>This provider uses a custom keystore to create and store a key with a known password.
*/
public class DebugKeyProvider {
-
+
public interface IKeyGenOutput {
public void out(String message);
public void err(String message);
}
-
+
private static final String PASSWORD_STRING = "android";
private static final char[] PASSWORD_CHAR = PASSWORD_STRING.toCharArray();
private static final String DEBUG_ALIAS = "AndroidDebugKey";
-
+
// Certificate CN value. This is a hard-coded value for the debug key.
// Android Market checks against this value in order to refuse applications signed with
// debug keys.
private static final String CERTIFICATE_DESC = "CN=Android Debug,O=Android,C=US";
-
+
private KeyStore.PrivateKeyEntry mEntry;
-
+
public static class KeytoolException extends Exception {
/** default serial uid */
private static final long serialVersionUID = 1L;
private String mJavaHome = null;
private String mCommandLine = null;
-
+
KeytoolException(String message) {
super(message);
}
KeytoolException(String message, String javaHome, String commandLine) {
super(message);
-
+
mJavaHome = javaHome;
mCommandLine = commandLine;
}
-
+
public String getJavaHome() {
return mJavaHome;
}
-
+
public String getCommandLine() {
return mCommandLine;
}
}
-
+
/**
* Creates a provider using a keystore at the given location.
* <p/>The keystore, and a new random android debug key are created if they do not yet exist.
@@ -91,16 +91,16 @@ public class DebugKeyProvider {
* @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr
* of the keytool process call.
* @throws KeytoolException If the creation of the debug key failed.
- * @throws AndroidLocationException
+ * @throws AndroidLocationException
*/
public DebugKeyProvider(String osKeyStorePath, String storeType, IKeyGenOutput output)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
UnrecoverableEntryException, IOException, KeytoolException, AndroidLocationException {
-
+
if (osKeyStorePath == null) {
osKeyStorePath = getDefaultKeyStoreOsPath();
}
-
+
if (loadKeyEntry(osKeyStorePath, storeType) == false) {
// create the store with the key
createNewStore(osKeyStorePath, storeType, output);
@@ -109,7 +109,7 @@ public class DebugKeyProvider {
/**
* Returns the OS path to the default debug keystore.
- *
+ *
* @return The OS path to the default debug keystore.
* @throws KeytoolException
* @throws AndroidLocationException
@@ -134,7 +134,7 @@ public class DebugKeyProvider {
if (mEntry != null) {
return mEntry.getPrivateKey();
}
-
+
return null;
}
@@ -150,7 +150,7 @@ public class DebugKeyProvider {
return null;
}
-
+
/**
* Loads the debug key from the keystore.
* @param osKeyStorePath the OS path to the keystore.
@@ -172,7 +172,7 @@ public class DebugKeyProvider {
} catch (FileNotFoundException e) {
return false;
}
-
+
return true;
}
@@ -193,9 +193,9 @@ public class DebugKeyProvider {
private void createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
UnrecoverableEntryException, IOException, KeytoolException {
-
+
if (KeystoreHelper.createNewStore(osKeyStorePath, storeType, PASSWORD_STRING, DEBUG_ALIAS,
- PASSWORD_STRING, CERTIFICATE_DESC, 1 /* validity*/, output)) {
+ PASSWORD_STRING, CERTIFICATE_DESC, 30 /* validity*/, output)) {
loadKeyEntry(osKeyStorePath, storeType);
}
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
index ee2a5a6..f05e9a6 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
@@ -16,10 +16,10 @@
package com.android.sdklib.internal.export;
+import com.android.io.FileWrapper;
+import com.android.io.IAbstractFile;
+import com.android.io.StreamException;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.FileWrapper;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.StreamException;
import com.android.sdklib.xml.AndroidManifestParser;
import com.android.sdklib.xml.ManifestData;
import com.android.sdklib.xml.ManifestData.SupportsScreens;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
index 16dd8eb..7840b91 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
@@ -16,6 +16,7 @@
package com.android.sdklib.internal.project;
+import com.android.AndroidConstants;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
@@ -325,11 +326,11 @@ public class ProjectCreator {
if (isTestProject == false) {
/* Make res files only for non test projects */
- File valueFolder = createDirs(resourceFolder, SdkConstants.FD_VALUES);
+ File valueFolder = createDirs(resourceFolder, AndroidConstants.FD_RES_VALUES);
installTargetTemplate("strings.template", new File(valueFolder, "strings.xml"),
keywords, target);
- File layoutFolder = createDirs(resourceFolder, SdkConstants.FD_LAYOUT);
+ File layoutFolder = createDirs(resourceFolder, AndroidConstants.FD_RES_LAYOUT);
installTargetTemplate("layout.template", new File(layoutFolder, "main.xml"),
keywords, target);
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
index 19cad00..11cd277 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
@@ -16,12 +16,12 @@
package com.android.sdklib.internal.project;
+import com.android.io.FolderWrapper;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.io.StreamException;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.FolderWrapper;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.IAbstractFolder;
-import com.android.sdklib.io.StreamException;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java
index 23cdd09..14cac2a 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java
@@ -16,11 +16,11 @@
package com.android.sdklib.internal.project;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.io.StreamException;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.IAbstractFolder;
-import com.android.sdklib.io.StreamException;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java
index 3fab9ce..ba2d501 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java
@@ -54,7 +54,11 @@ public class AdbWrapper {
}
private void display(String format, Object...args) {
- mMonitor.setResult(format, args);
+ mMonitor.log(format, args);
+ }
+
+ private void displayError(String format, Object...args) {
+ mMonitor.logError(format, args);
}
/**
@@ -63,7 +67,7 @@ public class AdbWrapper {
*/
public synchronized boolean startAdb() {
if (mAdbOsLocation == null) {
- display("Error: missing path to ADB."); //$NON-NLS-1$
+ displayError("Error: missing path to ADB."); //$NON-NLS-1$
return false;
}
@@ -82,15 +86,15 @@ public class AdbWrapper {
false /* waitForReaders */);
} catch (IOException ioe) {
- display("Unable to run 'adb': %1$s.", ioe.getMessage()); //$NON-NLS-1$
+ displayError("Unable to run 'adb': %1$s.", ioe.getMessage()); //$NON-NLS-1$
// we'll return false;
} catch (InterruptedException ie) {
- display("Unable to run 'adb': %1$s.", ie.getMessage()); //$NON-NLS-1$
+ displayError("Unable to run 'adb': %1$s.", ie.getMessage()); //$NON-NLS-1$
// we'll return false;
}
if (status != 0) {
- display("'adb start-server' failed."); //$NON-NLS-1$
+ displayError("'adb start-server' failed."); //$NON-NLS-1$
return false;
}
@@ -105,7 +109,7 @@ public class AdbWrapper {
*/
public synchronized boolean stopAdb() {
if (mAdbOsLocation == null) {
- display("Error: missing path to ADB."); //$NON-NLS-1$
+ displayError("Error: missing path to ADB."); //$NON-NLS-1$
return false;
}
@@ -127,7 +131,7 @@ public class AdbWrapper {
}
if (status != 0) {
- display("'adb kill-server' failed -- run manually if necessary."); //$NON-NLS-1$
+ displayError("'adb kill-server' failed -- run manually if necessary."); //$NON-NLS-1$
return false;
}
@@ -163,7 +167,7 @@ public class AdbWrapper {
while (true) {
String line = errReader.readLine();
if (line != null) {
- display("ADB Error: %1$s", line);
+ displayError("ADB Error: %1$s", line);
errorOutput.add(line);
} else {
break;
@@ -185,7 +189,7 @@ public class AdbWrapper {
while (true) {
String line = outReader.readLine();
if (line != null) {
- display("ADB: %1$s", line);
+ displayError("ADB: %1$s", line);
stdOutput.add(line);
} else {
break;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
index bed9174..526bfcb 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
@@ -247,7 +247,22 @@ public class AddonPackage extends Package
return mLibs;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ return String.format("%1$s by %2$s%3$s",
+ getName(),
+ getVendor(),
+ isObsolete() ? " (Obsolete)" : "");
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
return String.format("%1$s by %2$s, Android API %3$s, revision %4$s%5$s",
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java
index 9b8d808..c0b7041 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java
@@ -142,11 +142,11 @@ public class AddonsListFetcher {
reason = String.format("Unknown (%1$s)", exception[0].getClass().getName());
}
- monitor.setResult("Failed to fetch URL %1$s, reason: %2$s", url, reason);
+ monitor.logError("Failed to fetch URL %1$s, reason: %2$s", url, reason);
}
if (validationError[0] != null) {
- monitor.setResult("%s", validationError[0]); //$NON-NLS-1$
+ monitor.logError("%s", validationError[0]); //$NON-NLS-1$
}
// Stop here if we failed to validate the XML. We don't want to load it.
@@ -409,13 +409,13 @@ public class AddonsListFetcher {
return doc;
} catch (ParserConfigurationException e) {
- monitor.setResult("Failed to create XML document builder");
+ monitor.logError("Failed to create XML document builder");
} catch (SAXException e) {
- monitor.setResult("Failed to parse XML document");
+ monitor.logError("Failed to parse XML document");
} catch (IOException e) {
- monitor.setResult("Failed to read XML document");
+ monitor.logError("Failed to read XML document");
}
return null;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
index f3fd347..5807ca3 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
@@ -63,7 +63,7 @@ public class ArchiveInstaller {
String name = pkg.getShortDescription();
if (pkg instanceof ExtraPackage && !((ExtraPackage) pkg).isPathValid()) {
- monitor.setResult("Skipping %1$s: %2$s is not a valid install path.",
+ monitor.log("Skipping %1$s: %2$s is not a valid install path.",
name,
((ExtraPackage) pkg).getPath());
return false;
@@ -71,14 +71,14 @@ public class ArchiveInstaller {
if (archive.isLocal()) {
// This should never happen.
- monitor.setResult("Skipping already installed archive: %1$s for %2$s",
+ monitor.log("Skipping already installed archive: %1$s for %2$s",
name,
archive.getOsDescription());
return false;
}
if (!archive.isCompatible()) {
- monitor.setResult("Skipping incompatible archive: %1$s for %2$s",
+ monitor.log("Skipping incompatible archive: %1$s for %2$s",
name,
archive.getOsDescription());
return false;
@@ -88,7 +88,7 @@ public class ArchiveInstaller {
if (archiveFile != null) {
// Unarchive calls the pre/postInstallHook methods.
if (unarchive(archive, osSdkRoot, archiveFile, sdkManager, monitor)) {
- monitor.setResult("Installed %1$s", name);
+ monitor.log("Installed %1$s", name);
// Delete the temp archive if it exists, only on success
OsHelper.deleteFileOrFolder(archiveFile);
return true;
@@ -110,7 +110,7 @@ public class ArchiveInstaller {
String name = archive.getParentPackage().getShortDescription();
String desc = String.format("Downloading %1$s", name);
monitor.setDescription(desc);
- monitor.setResult(desc);
+ monitor.log(desc);
String link = archive.getUrl();
if (!link.startsWith("http://") //$NON-NLS-1$
@@ -120,7 +120,7 @@ public class ArchiveInstaller {
Package pkg = archive.getParentPackage();
SdkSource src = pkg.getParentSource();
if (src == null) {
- monitor.setResult("Internal error: no source for archive %1$s", name);
+ monitor.logError("Internal error: no source for archive %1$s", name);
return null;
}
@@ -152,7 +152,7 @@ public class ArchiveInstaller {
OsHelper.deleteFileOrFolder(tmpFolder);
}
if (!tmpFolder.mkdirs()) {
- monitor.setResult("Failed to create directory %1$s", tmpFolder.getPath());
+ monitor.logError("Failed to create directory %1$s", tmpFolder.getPath());
return null;
}
}
@@ -161,7 +161,7 @@ public class ArchiveInstaller {
// if the file exists, check its checksum & size. Use it if complete
if (tmpFile.exists()) {
if (tmpFile.length() == archive.getSize()) {
- String chksum = "";
+ String chksum = ""; //$NON-NLS-1$
try {
chksum = fileChecksum(archive.getChecksumType().getMessageDigest(),
tmpFile,
@@ -214,10 +214,10 @@ public class ArchiveInstaller {
} catch (FileNotFoundException e) {
// The FNF message is just the URL. Make it a bit more useful.
- monitor.setResult("File not found: %1$s", e.getMessage());
+ monitor.logError("File not found: %1$s", e.getMessage());
} catch (Exception e) {
- monitor.setResult(e.getMessage());
+ monitor.logError(e.getMessage());
} finally {
if (is != null) {
@@ -327,14 +327,15 @@ public class ArchiveInstaller {
}
if (monitor.isCancelRequested()) {
- monitor.setResult("Download aborted by user at %1$d bytes.", total);
+ monitor.log("Download aborted by user at %1$d bytes.", total);
return false;
}
}
if (total != size) {
- monitor.setResult("Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
+ monitor.logError(
+ "Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
size, total);
return false;
}
@@ -343,7 +344,7 @@ public class ArchiveInstaller {
String actual = getDigestChecksum(digester);
String expected = archive.getChecksum();
if (!actual.equalsIgnoreCase(expected)) {
- monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
+ monitor.logError("Download finished with wrong checksum. Expected %1$s, got %2$s.",
expected, actual);
return false;
}
@@ -352,10 +353,10 @@ public class ArchiveInstaller {
} catch (FileNotFoundException e) {
// The FNF message is just the URL. Make it a bit more useful.
- monitor.setResult("File not found: %1$s", e.getMessage());
+ monitor.logError("File not found: %1$s", e.getMessage());
} catch (Exception e) {
- monitor.setResult(e.getMessage());
+ monitor.logError(e.getMessage());
} finally {
if (os != null) {
@@ -391,7 +392,7 @@ public class ArchiveInstaller {
String pkgName = pkg.getShortDescription();
String pkgDesc = String.format("Installing %1$s", pkgName);
monitor.setDescription(pkgDesc);
- monitor.setResult(pkgDesc);
+ monitor.log(pkgDesc);
// Ideally we want to always unzip in a temp folder which name depends on the package
// type (e.g. addon, tools, etc.) and then move the folder to the destination folder.
@@ -432,12 +433,12 @@ public class ArchiveInstaller {
if (destFolder == null) {
// this should not seriously happen.
- monitor.setResult("Failed to compute installation directory for %1$s.", pkgName);
+ monitor.log("Failed to compute installation directory for %1$s.", pkgName);
return false;
}
if (!pkg.preInstallHook(archive, monitor, osSdkRoot, destFolder)) {
- monitor.setResult("Skipping archive: %1$s", pkgName);
+ monitor.log("Skipping archive: %1$s", pkgName);
return false;
}
@@ -450,14 +451,14 @@ public class ArchiveInstaller {
}
if (oldDestFolder == null) {
// this should not seriously happen.
- monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot);
+ monitor.logError("Failed to find a temp directory in %1$s.", osSdkRoot);
return false;
}
// Try to move the current dest dir to the temp/old one. Tell the user if it failed.
while(true) {
if (!moveFolder(destFolder, oldDestFolder)) {
- monitor.setResult("Failed to rename directory %1$s to %2$s.",
+ monitor.logError("Failed to rename directory %1$s to %2$s.",
destFolder.getPath(), oldDestFolder.getPath());
if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) {
@@ -489,7 +490,7 @@ public class ArchiveInstaller {
// -2- Unzip new content directly in place.
if (!destFolder.mkdirs()) {
- monitor.setResult("Failed to create directory %1$s", destFolder.getPath());
+ monitor.logError("Failed to create directory %1$s", destFolder.getPath());
return false;
}
@@ -498,7 +499,7 @@ public class ArchiveInstaller {
}
if (!generateSourceProperties(archive, destFolder)) {
- monitor.setResult("Failed to generate source.properties in directory %1$s",
+ monitor.logError("Failed to generate source.properties in directory %1$s",
destFolder.getPath());
return false;
}
@@ -627,7 +628,7 @@ public class ArchiveInstaller {
// Create directory if it doesn't exist yet. This allows us to create
// empty directories.
if (!destFile.isDirectory() && !destFile.mkdirs()) {
- monitor.setResult("Failed to create temp directory %1$s",
+ monitor.logError("Failed to create temp directory %1$s",
destFile.getPath());
return false;
}
@@ -638,7 +639,7 @@ public class ArchiveInstaller {
File parentDir = destFile.getParentFile();
if (!parentDir.isDirectory()) {
if (!parentDir.mkdirs()) {
- monitor.setResult("Failed to create temp directory %1$s",
+ monitor.logError("Failed to create temp directory %1$s",
parentDir.getPath());
return false;
}
@@ -689,7 +690,7 @@ public class ArchiveInstaller {
return true;
} catch (IOException e) {
- monitor.setResult("Unzip failed: %1$s", e.getMessage());
+ monitor.logError("Unzip failed: %1$s", e.getMessage());
} finally {
if (zipFile != null) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java
index 1629045..ca6f463 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java
@@ -99,7 +99,19 @@ public class BrokenPackage extends Package
return mExactApiLevel;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ return mShortDescription;
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
return mShortDescription;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
index 8a4c19d..5171454 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
@@ -117,13 +117,35 @@ public class DocPackage extends Package implements IPackageVersion {
mVersion.saveProperties(props);
}
- /** Returns the version, for platform, add-on and doc packages.
- * Can be 0 if this is a local package of unknown api-level. */
+ /**
+ * Returns the version, for platform, add-on and doc packages.
+ * Can be 0 if this is a local package of unknown api-level.
+ */
public AndroidVersion getVersion() {
return mVersion;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ if (mVersion.isPreview()) {
+ return String.format("Documentation for Android '%1$s' Preview SDK%2$s",
+ mVersion.getCodename(),
+ isObsolete() ? " (Obsolete)" : "");
+ } else {
+ return String.format("Documentation for Android SDK%2$s",
+ mVersion.getApiLevel(),
+ isObsolete() ? " (Obsolete)" : "");
+ }
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
if (mVersion.isPreview()) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
index 49236dc..bdf2805 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
@@ -253,9 +253,7 @@ public class ExtraPackage extends MinToolsPackage
return ""; //$NON-NLS-1$
}
- /** Returns a short description for an {@link IDescription}. */
- @Override
- public String getShortDescription() {
+ private String getPrettyName() {
String name = mPath;
// In the past, we used to save the extras in a folder vendor-path,
@@ -299,8 +297,31 @@ public class ExtraPackage extends MinToolsPackage
name = name.replaceAll(" Usb ", " USB "); //$NON-NLS-1$
name = name.replaceAll(" Api ", " API "); //$NON-NLS-1$
+ return name;
+ }
+
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ String s = String.format("%1$s package%2$s",
+ getPrettyName(),
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
+
+ return s;
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
+ @Override
+ public String getShortDescription() {
+
String s = String.format("%1$s package, revision %2$d%3$s",
- name,
+ getPrettyName(),
getRevision(),
isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
@@ -394,19 +415,33 @@ public class ExtraPackage extends MinToolsPackage
ExtraPackage ep = (ExtraPackage) pkg;
// To be backward compatible, we need to support the old vendor-path form
- if (ep.mPath != null && (ep.mVendor == null || ep.mVendor.length() == 0) &&
- mPath != null && mVendor != null) {
- if (ep.mPath.equals(mVendor + "-" + mPath)) { //$NON-NLS-1$
+ // in either the current or the remote package.
+ //
+ // The vendor test below needs to account for an old installed package
+ // (e.g. with an install path of vendor-name) that has then beeen updated
+ // in-place and thus when reloaded contains the vendor name in both the
+ // path and the vendor attributes.
+ if (ep.mPath != null && mPath != null && mVendor != null) {
+ if (ep.mPath.equals(mVendor + "-" + mPath) && //$NON-NLS-1$
+ (ep.mVendor == null || ep.mVendor.length() == 0
+ || ep.mVendor.equals(mVendor))) {
+ return true;
+ }
+ }
+ if (mPath != null && ep.mPath != null && ep.mVendor != null) {
+ if (mPath.equals(ep.mVendor + "-" + ep.mPath) && //$NON-NLS-1$
+ (mVendor == null || mVendor.length() == 0 || mVendor.equals(ep.mVendor))) {
return true;
}
}
+
if (!mPath.equals(ep.mPath)) {
return false;
}
if ((mVendor == null && ep.mVendor == null) ||
- (mVendor != null && !mVendor.equals(ep.mVendor))) {
- return false;
+ (mVendor != null && mVendor.equals(ep.mVendor))) {
+ return true;
}
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java
index e08e27c..40f1ddb 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java
@@ -27,6 +27,21 @@ package com.android.sdklib.internal.repository;
* If the task runs in a non-UI worker thread, the task factory implementation
* will take care of the update the UI in the correct thread. The task itself
* must not have to deal with it.
+ * <p/>
+ * A monitor typically has 3 levels of text displayed: <br/>
+ * - A <b>title</b> <em>may</em> be present on a task dialog, typically when a task
+ * dialog is created. This is not covered by this monitor interface. <br/>
+ * - A <b>description</b> displays prominent information on what the task
+ * is currently doing. This is expected to vary over time, typically changing
+ * with each sub-monitor, and typically only the last description is visible.
+ * For example an updater would typically have descriptions such as "downloading",
+ * "installing" and finally "done". This is set using {@link #setDescription}. <br/>
+ * - A <b>verbose</b> optional log that can provide more information than the summary
+ * description and is typically displayed in some kind of scrollable multi-line
+ * text field so that the user can keep track of what happened. 3 levels are
+ * provided: error, normal and verbose. An UI may hide the log till an error is
+ * logged and/or might hide the verbose text unless a flag is checked by the user.
+ * This is set using {@link #log}, {@link #logError} and {@link #logVerbose}.
*/
public interface ITaskMonitor {
@@ -34,13 +49,26 @@ public interface ITaskMonitor {
* Sets the description in the current task dialog.
* This method can be invoked from a non-UI thread.
*/
- public void setDescription(String descriptionFormat, Object...args);
+ public void setDescription(String format, Object...args);
+
+ /**
+ * Logs a "normal" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void log(String format, Object...args);
+
+ /**
+ * Logs an "error" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logError(String format, Object...args);
/**
- * Sets the result text in the current task dialog.
+ * Logs a "verbose" information line, that is extra details which are typically
+ * not that useful for the end-user and might be hidden until explicitly shown.
* This method can be invoked from a non-UI thread.
*/
- public void setResult(String resultFormat, Object...args);
+ public void logVerbose(String format, Object...args);
/**
* Sets the max value of the progress bar.
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
index eb00562..c4b92b5 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
@@ -303,8 +303,12 @@ public class LocalSdkParser {
names.add(file.getName());
}
}
+
+ final String emulatorBinName =
+ SdkConstants.FN_EMULATOR + SdkConstants.FN_EMULATOR_EXTENSION;
+
if (!names.contains(SdkConstants.androidCmdName()) ||
- !names.contains(SdkConstants.FN_EMULATOR)) {
+ !names.contains(emulatorBinName)) {
return null;
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java
index 02cebe1..c2338f3 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java
@@ -22,6 +22,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
/**
@@ -30,6 +32,21 @@ import java.io.IOException;
abstract class OsHelper {
/**
+ * Reflection method for File.setExecutable(boolean, boolean). Only present in Java 6.
+ */
+ private static Method sFileSetExecutable = null;
+ /**
+ * Whether File.setExecutable was queried through reflection. This is to only
+ * attempt reflection once.
+ */
+ private static boolean sFileReflectionDone = false;
+ /**
+ * Parameters to call File.setExecutable through reflection.
+ */
+ private final static Object[] sFileSetExecutableParams = new Object[] {
+ Boolean.TRUE, Boolean.FALSE };
+
+ /**
* Helper to delete a file or a directory.
* For a directory, recursively deletes all of its content.
* Files that cannot be deleted right away are marked for deletion on exit.
@@ -76,18 +93,49 @@ abstract class OsHelper {
}
/**
- * Sets the executable Unix permission (0777) on a file or folder.
+ * Sets the executable Unix permission (+x) on a file or folder.
+ * <p/>
+ * This attempts to use {@link File#setExecutable(boolean, boolean)} through reflection if
+ * it's available.
+ * If this is not available, this invokes a chmod exec instead,
+ * so there is no guarantee of it being fast.
* <p/>
- * This invokes a chmod exec, so there is no guarantee of it being fast.
* Caller must make sure to not invoke this under Windows.
*
* @param file The file to set permissions on.
* @throws IOException If an I/O error occurs
*/
static void setExecutablePermission(File file) throws IOException {
+ if (sFileReflectionDone == false) {
+ try {
+ sFileSetExecutable = File.class.getMethod("setExecutable", //$NON-NLS-1$
+ boolean.class, boolean.class);
+
+ } catch (SecurityException e) {
+ // do nothing we'll use chdmod instead
+ } catch (NoSuchMethodException e) {
+ // do nothing we'll use chdmod instead
+ }
+
+ sFileReflectionDone = true;
+ }
+
+ if (sFileSetExecutable != null) {
+ try {
+ sFileSetExecutable.invoke(file, sFileSetExecutableParams);
+ return;
+ } catch (IllegalArgumentException e) {
+ // we'll run chmod below
+ } catch (IllegalAccessException e) {
+ // we'll run chmod below
+ } catch (InvocationTargetException e) {
+ // we'll run chmod below
+ }
+ }
+
Runtime.getRuntime().exec(new String[] {
- "chmod", "777", file.getAbsolutePath()
- });
+ "chmod", "+x", file.getAbsolutePath() //$NON-NLS-1$ //$NON-NLS-2$
+ });
}
/**
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
index 58be3c9..c597ad8 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
@@ -372,6 +372,17 @@ public abstract class Package implements IDescription, Comparable<Package> {
}
/**
+ * Returns a description of this package that is suitable for a list display.
+ * Should not be empty. Must never be null.
+ * <p/>
+ * Note that this is the "base" name for the package
+ * with no specific revision nor API mentionned.
+ * In contrast, {@link #getShortDescription()} should be used if you want more details
+ * such as the package revision number or the API, if applicable.
+ */
+ public abstract String getListDescription();
+
+ /**
* Returns a short description for an {@link IDescription}.
* Can be empty but not null.
*/
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
index c303e2f..622a922 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
@@ -121,7 +121,31 @@ public class PlatformPackage extends MinToolsPackage implements IPackageVersion
return mVersion;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ String s;
+
+ if (mVersion.isPreview()) {
+ s = String.format("SDK Platform Android %1$s Preview%2$s",
+ getVersionName(),
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
+ } else {
+ s = String.format("SDK Platform Android %1$s%2$s",
+ getVersionName(),
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
+ }
+
+ return s;
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
String s;
@@ -130,13 +154,13 @@ public class PlatformPackage extends MinToolsPackage implements IPackageVersion
s = String.format("SDK Platform Android %1$s Preview, revision %2$s%3$s",
getVersionName(),
getRevision(),
- isObsolete() ? " (Obsolete)" : "");
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
} else {
s = String.format("SDK Platform Android %1$s, API %2$d, revision %3$s%4$s",
getVersionName(),
mVersion.getApiLevel(),
getRevision(),
- isObsolete() ? " (Obsolete)" : "");
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
}
return s;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java
index 860d703..9a2de62 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java
@@ -143,7 +143,20 @@ public class PlatformToolPackage extends Package {
archiveOsPath);
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ return String.format("Android SDK Platform-tools%1$s",
+ isObsolete() ? " (Obsolete)" : "");
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
return String.format("Android SDK Platform-tools, revision %1$d%2$s",
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java
index 035677b..b436b91 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java
@@ -175,7 +175,23 @@ public class SamplePackage extends MinToolsPackage
return mVersion;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ String s = String.format("Samples for SDK API %1$s%2$s%3$s",
+ mVersion.getApiString(),
+ mVersion.isPreview() ? " Preview" : "",
+ isObsolete() ? " (Obsolete)" : "");
+ return s;
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
String s = String.format("Samples for SDK API %1$s%2$s, revision %3$d%4$s",
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java
index 58bf314..22b9c42 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java
@@ -57,7 +57,7 @@ import javax.xml.validation.Validator;
* It may be a full repository or an add-on only repository.
* A repository describes one or {@link Package}s available for download.
*/
-public abstract class SdkSource implements IDescription {
+public abstract class SdkSource implements IDescription, Comparable<SdkSource> {
private String mUrl;
@@ -151,6 +151,14 @@ public abstract class SdkSource implements IDescription {
}
/**
+ * Implementation of the {@link Comparable} interface.
+ * Simply compares the URL using the string's default ordering.
+ */
+ public int compareTo(SdkSource rhs) {
+ return this.getUrl().compareTo(rhs.getUrl());
+ }
+
+ /**
* Returns the UI-visible name of the source. Can be null.
*/
public String getUiName() {
@@ -250,7 +258,7 @@ public abstract class SdkSource implements IDescription {
url = url.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$
}
- monitor.setDescription("Fetching %1$s", url);
+ monitor.setDescription("Fetching URL: %1$s", url);
monitor.incProgress(1);
mFetchError = null;
@@ -276,7 +284,7 @@ public abstract class SdkSource implements IDescription {
}
if (xml != null) {
- monitor.setDescription("Validate XML");
+ monitor.setDescription(String.format("Validate XML: %1$s", url));
for (int tryOtherUrl = 0; tryOtherUrl < 2; tryOtherUrl++) {
// Explore the XML to find the potential XML schema version
@@ -295,7 +303,7 @@ public abstract class SdkSource implements IDescription {
if (usingAlternateUrl && validatedDoc != null) {
// If the second tentative succeeded, indicate it in the console
// with the URL that worked.
- monitor.setResult("Repository found at %1$s", url);
+ monitor.log("Repository found at %1$s", url);
// Keep the modified URL
mUrl = url;
@@ -380,11 +388,11 @@ public abstract class SdkSource implements IDescription {
reason = String.format("Unknown (%1$s)", exception[0].getClass().getName());
}
- monitor.setResult("Failed to fetch URL %1$s, reason: %2$s", url, reason);
+ monitor.logError("Failed to fetch URL %1$s, reason: %2$s", url, reason);
}
if (validationError[0] != null) {
- monitor.setResult("%s", validationError[0]); //$NON-NLS-1$
+ monitor.logError("%s", validationError[0]); //$NON-NLS-1$
}
// Stop here if we failed to validate the XML. We don't want to load it.
@@ -424,7 +432,7 @@ public abstract class SdkSource implements IDescription {
monitor.incProgress(1);
if (xml != null) {
- monitor.setDescription("Parse XML");
+ monitor.setDescription(String.format("Parse XML: %1$s", url));
monitor.incProgress(1);
parsePackages(validatedDoc, validatedUri, monitor);
if (mPackages == null || mPackages.length == 0) {
@@ -740,12 +748,11 @@ public abstract class SdkSource implements IDescription {
if (p != null) {
packages.add(p);
- monitor.setDescription("Found %1$s", p.getShortDescription());
+ monitor.logVerbose("Found %1$s", p.getShortDescription());
}
} catch (Exception e) {
// Ignore invalid packages
- monitor.setResult("Ignoring invalid %1$s element: %2$s",
- name, e.toString());
+ monitor.logError("Ignoring invalid %1$s element: %2$s", name, e.toString());
}
}
}
@@ -794,13 +801,13 @@ public abstract class SdkSource implements IDescription {
return doc;
} catch (ParserConfigurationException e) {
- monitor.setResult("Failed to create XML document builder");
+ monitor.logError("Failed to create XML document builder");
} catch (SAXException e) {
- monitor.setResult("Failed to parse XML document");
+ monitor.logError("Failed to parse XML document");
} catch (IOException e) {
- monitor.setResult("Failed to read XML document");
+ monitor.logError("Failed to read XML document");
}
return null;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java
index 22678b3..be99f22 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java
@@ -111,7 +111,7 @@ public class SdkSources {
}
/**
- * Returns an array of sources attached to the given category.
+ * Returns a new array of sources attached to the given category.
* Might return an empty array, but never returns null.
*/
public SdkSource[] getSources(SdkSourceCategory category) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
index 6e53dd7..69039ea 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
@@ -151,7 +151,20 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency
return mMinPlatformToolsRevision;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ return String.format("Android SDK Tools%1$s",
+ isObsolete() ? " (Obsolete)" : "");
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
return String.format("Android SDK Tools, revision %1$d%2$s",
@@ -250,17 +263,17 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency
status = grabProcessOutput(proc, monitor, scriptName);
} catch (Exception e) {
- monitor.setResult("Exception: %s", e.toString());
+ monitor.logError("Exception: %s", e.toString());
}
if (status != 0) {
- monitor.setResult("Failed to execute %s", scriptName);
+ monitor.logError("Failed to execute %s", scriptName);
return;
}
}
/**
- * Get the stderr/stdout outputs of a process and return when the process is done.
+ * Gets the stderr/stdout outputs of a process and returns when the process is done.
* Both <b>must</b> be read or the process will block on windows.
* @param process The process to get the ouput from.
* @param monitor The monitor where to output errors.
@@ -285,7 +298,7 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency
while (true) {
String line = errReader.readLine();
if (line != null) {
- monitor.setResult("[%1$s] Error: %2$s", scriptName, line);
+ monitor.logError("[%1$s] Error: %2$s", scriptName, line);
} else {
break;
}
@@ -306,7 +319,7 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency
while (true) {
String line = outReader.readLine();
if (line != null) {
- monitor.setResult("[%1$s] %2$s", scriptName, line);
+ monitor.log("[%1$s] %2$s", scriptName, line);
} else {
break;
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java
index 2cb6ace..26fcd7b 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java
@@ -16,10 +16,10 @@
package com.android.sdklib.xml;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.io.StreamException;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.IAbstractFolder;
-import com.android.sdklib.io.StreamException;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
@@ -76,6 +76,7 @@ public final class AndroidManifest {
public final static String ATTRIBUTE_REQ_HARDKEYBOARD = "reqHardKeyboard";
public final static String ATTRIBUTE_REQ_KEYBOARDTYPE = "reqKeyboardType";
public final static String ATTRIBUTE_REQ_TOUCHSCREEN = "reqTouchScreen";
+ public static final String ATTRIBUTE_THEME = "theme";
/**
* Returns an {@link IAbstractFile} object representing the manifest for the given project.
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java
index 09e81f7..ca59a8e 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java
@@ -16,13 +16,13 @@
package com.android.sdklib.xml;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.io.StreamException;
import com.android.resources.Keyboard;
import com.android.resources.Navigation;
import com.android.resources.TouchScreen;
import com.android.sdklib.SdkConstants;
-import com.android.sdklib.io.IAbstractFile;
-import com.android.sdklib.io.IAbstractFolder;
-import com.android.sdklib.io.StreamException;
import com.android.sdklib.xml.ManifestData.Activity;
import com.android.sdklib.xml.ManifestData.Instrumentation;
import com.android.sdklib.xml.ManifestData.SupportsScreens;
diff --git a/sdkmanager/libs/sdklib/tests/Android.mk b/sdkmanager/libs/sdklib/tests/Android.mk
new file mode 100644
index 0000000..0179dc1
--- /dev/null
+++ b/sdkmanager/libs/sdklib/tests/Android.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2011 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)
+
+# Only compile source java files in this lib.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+LOCAL_MODULE := sdklib-tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := sdklib junit
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/build/DebugKeyProviderTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/build/DebugKeyProviderTest.java
new file mode 100755
index 0000000..6f6896e
--- /dev/null
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/build/DebugKeyProviderTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdklib.internal.build;
+
+import com.android.sdklib.internal.build.DebugKeyProvider.IKeyGenOutput;
+
+import java.io.File;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+public class DebugKeyProviderTest extends TestCase {
+
+ private File mTmpFile;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // We want to allocate a new tmp file but not have it actually exist
+ mTmpFile = File.createTempFile(this.getClass().getSimpleName(), ".keystore");
+ assertTrue(mTmpFile.delete());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ if (mTmpFile != null) {
+ if (!mTmpFile.delete()) {
+ mTmpFile.deleteOnExit();
+ }
+ mTmpFile = null;
+ }
+ }
+
+ public void testCreateAndCheckKey() throws Exception {
+ String osPath = mTmpFile.getAbsolutePath();
+
+ KeygenOutput keygenOutput = new KeygenOutput();
+
+ // "now" is just slightly before the key was created
+ long now = System.currentTimeMillis();
+
+ DebugKeyProvider provider;
+ try {
+ provider = new DebugKeyProvider(osPath, null /*storeType*/, keygenOutput);
+ } catch (Throwable t) {
+ // In case we get any kind of exception, rewrap it to make sure we output
+ // the path used.
+ String msg = String.format("%1$s in %2$s\n%3$s",
+ t.getClass().getSimpleName(), osPath, t.toString());
+ throw new Exception(msg, t);
+ }
+ assertNotNull(provider);
+
+ assertEquals("", keygenOutput.getOut());
+ assertEquals("", keygenOutput.getErr());
+
+ PrivateKey key = provider.getDebugKey();
+ assertNotNull(key);
+
+ X509Certificate certificate = (X509Certificate) provider.getCertificate();
+ assertNotNull(certificate);
+
+ // The "not-after" (a.k.a. expiration) date should be after "now"
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(now);
+ assertTrue(certificate.getNotAfter().compareTo(c.getTime()) > 0);
+
+ // It should be valid after 1 year from now (adjust by a second since the 'now' time
+ // doesn't exactly match the creation time... 1 second should be enough.)
+ c.add(Calendar.DAY_OF_YEAR, 365);
+ c.add(Calendar.SECOND, -1);
+ assertTrue("1 year expiration failed",
+ certificate.getNotAfter().compareTo(c.getTime()) > 0);
+
+ // and 30 years from now
+ c.add(Calendar.DAY_OF_YEAR, 29 * 365);
+ assertTrue("30 year expiration failed",
+ certificate.getNotAfter().compareTo(c.getTime()) > 0);
+
+ // however expiration date should be passed in 30 years + 1 hour
+ c.add(Calendar.HOUR, 1);
+ assertFalse("30 year and 1 hour expiration failed",
+ certificate.getNotAfter().compareTo(c.getTime()) > 0);
+ }
+
+ private static class KeygenOutput implements IKeyGenOutput {
+ private String mOut = ""; //$NON-NLS-1$
+ private String mErr = ""; //$NON-NLS-1$
+
+ public void out(String message) {
+ mOut += message + "\n"; //$NON-NLS-1$
+ }
+
+ public void err(String message) {
+ mErr += message + "\n"; //$NON-NLS-1$
+ }
+
+ public String getOut() {
+ return mOut;
+ }
+
+ public String getErr() {
+ return mErr;
+ }
+ }
+}
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/AddonsListFetcherTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/AddonsListFetcherTest.java
index 3f2bb84..9cf84a2 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/AddonsListFetcherTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/AddonsListFetcherTest.java
@@ -104,7 +104,9 @@ public class AddonsListFetcherTest extends TestCase {
Site[] result = mFetcher._parseAddonsList(doc, uri, monitor);
assertEquals("", monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
+ assertEquals("", monitor.getCapturedVerboseLog());
// check the sites we found...
assertEquals(3, result.length);
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/BrokenPackageTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/BrokenPackageTest.java
index 3e9ba8d..3e9ba8d 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/BrokenPackageTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/BrokenPackageTest.java
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockAddonPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java
index e99463b..55ace17 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockAddonPackage.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java
@@ -18,6 +18,7 @@ package com.android.sdklib.internal.repository;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import java.util.Map;
@@ -68,6 +69,14 @@ public class MockAddonPackage extends AddonPackage {
return "mock addon target";
}
+ public String[] getAbiList() {
+ return new String[] { SdkConstants.ABI_ARMEABI };
+ }
+
+ public String getImagePath(String abiType) {
+ return SdkConstants.OS_IMAGES_FOLDER;
+ }
+
public String getLocation() {
return "";
}
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockBrokenPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockBrokenPackage.java
index 289305b..289305b 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockBrokenPackage.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockBrokenPackage.java
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockEmptySdkManager.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockEmptySdkManager.java
index f66734b..f66734b 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockEmptySdkManager.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockEmptySdkManager.java
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockMonitor.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockMonitor.java
index 56a7c6c..3b7e608 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockMonitor.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockMonitor.java
@@ -24,26 +24,44 @@ package com.android.sdklib.internal.repository;
*/
public class MockMonitor implements ITaskMonitor {
- String mCapturedResults = "";
- String mCapturedDescriptions = "";
+ String mCapturedLog = ""; //$NON-NLS-1$
+ String mCapturedErrorLog = ""; //$NON-NLS-1$
+ String mCapturedVerboseLog = ""; //$NON-NLS-1$
+ String mCapturedDescriptions = ""; //$NON-NLS-1$
- public String getCapturedResults() {
- return mCapturedResults;
+ public String getCapturedLog() {
+ return mCapturedLog;
+ }
+
+ public String getCapturedErrorLog() {
+ return mCapturedErrorLog;
+ }
+
+ public String getCapturedVerboseLog() {
+ return mCapturedVerboseLog;
}
public String getCapturedDescriptions() {
return mCapturedDescriptions;
}
- public void setResult(String resultFormat, Object... args) {
- mCapturedResults += String.format(resultFormat, args) + "\n";
+ public void log(String format, Object... args) {
+ mCapturedLog += String.format(format, args) + "\n"; //$NON-NLS-1$
+ }
+
+ public void logError(String format, Object... args) {
+ mCapturedErrorLog += String.format(format, args) + "\n"; //$NON-NLS-1$
+ }
+
+ public void logVerbose(String format, Object... args) {
+ mCapturedVerboseLog += String.format(format, args) + "\n"; //$NON-NLS-1$
}
public void setProgressMax(int max) {
}
- public void setDescription(String descriptionFormat, Object... args) {
- mCapturedDescriptions += String.format(descriptionFormat, args) + "\n";
+ public void setDescription(String format, Object... args) {
+ mCapturedDescriptions += String.format(format, args) + "\n"; //$NON-NLS-1$
}
public boolean isCancelRequested() {
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformPackage.java
index 0a58487..ad1ab16 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformPackage.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformPackage.java
@@ -18,6 +18,7 @@ package com.android.sdklib.internal.repository;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import java.util.Map;
import java.util.Properties;
@@ -100,6 +101,14 @@ public class MockPlatformPackage extends PlatformPackage {
return "mock platform target";
}
+ public String[] getAbiList() {
+ return new String[] { SdkConstants.ABI_ARMEABI };
+ }
+
+ public String getImagePath(String abiType) {
+ return SdkConstants.OS_IMAGES_FOLDER;
+ }
+
public String getLocation() {
return "";
}
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformToolPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformToolPackage.java
index 0befa80..0befa80 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformToolPackage.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformToolPackage.java
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockToolPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockToolPackage.java
index 8ce704c..8ce704c 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockToolPackage.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockToolPackage.java
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkAddonSourceTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java
index ea6c4f6..6df440e 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkAddonSourceTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java
@@ -176,8 +176,9 @@ public class SdkAddonSourceTest extends TestCase {
"Found G USB Driver package, revision 43 (Obsolete)\n" +
"Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +
"Found Unkown Extra package, revision 2 (Obsolete)\n",
- monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 11 packages with each at least
// one archive.
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java
index aee811f..24a6fb6 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java
@@ -141,14 +141,15 @@ public class SdkRepoSourceTest extends TestCase {
Document result = mSource._findAlternateToolsXml(xmlStream);
assertNotNull(result);
- MockMonitor mon = new MockMonitor();
- assertTrue(mSource._parsePackages(result, SdkRepoConstants.NS_URI, mon));
+ MockMonitor monitor = new MockMonitor();
+ assertTrue(mSource._parsePackages(result, SdkRepoConstants.NS_URI, monitor));
assertEquals("Found Android SDK Tools, revision 1\n" +
"Found Android SDK Tools, revision 42\n" +
"Found Android SDK Platform-tools, revision 3\n",
- mon.getCapturedDescriptions());
- assertEquals("", mon.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 2 tool packages and 1
// platform-tools package, with at least 1 archive each.
@@ -199,8 +200,9 @@ public class SdkRepoSourceTest extends TestCase {
"Found Android SDK Tools, revision 42\n" +
"Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +
"Found Usb Driver package, revision 43\n",
- monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 11 packages with each at least
// one archive.
@@ -277,8 +279,9 @@ public class SdkRepoSourceTest extends TestCase {
"Found Usb Driver package, revision 43 (Obsolete)\n" +
"Found Extra API Dep package, revision 2 (Obsolete)\n" +
"Found Samples for SDK API 14, revision 24 (Obsolete)\n",
- monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 13 packages with each at least
// one archive.
@@ -353,8 +356,9 @@ public class SdkRepoSourceTest extends TestCase {
"Found A USB Driver package, revision 43 (Obsolete)\n" +
"Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +
"Found Samples for SDK API 14, revision 24 (Obsolete)\n",
- monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 13 packages with each at least
// one archive.
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/mock/MockLog.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/mock/MockLog.java
index 3ef0140..3ef0140 100644
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/mock/MockLog.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/mock/MockLog.java
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/SdkRepositoryTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/SdkRepositoryTest.java
index 2bbc088..2bbc088 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/SdkRepositoryTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/SdkRepositoryTest.java
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml
index f1dce67..f1dce67 100644
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp.xml
index f55b4e0..f55b4e0 100644
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp.xml
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp.xml
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp2.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp2.xml
index d5bcac7..d5bcac7 100644
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp2.xml
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp2.xml
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addon_sample_1.xml
index d761d73..d761d73 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addon_sample_1.xml
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addons_list_sample_1.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addons_list_sample_1.xml
index bda69d4..bda69d4 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addons_list_sample_1.xml
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addons_list_sample_1.xml
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_1.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_1.xml
index aa209d3..aa209d3 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_1.xml
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_1.xml
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_2.xml
index 4b8e329..4b8e329 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_2.xml
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_3.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_3.xml
index 05a9c79..05a9c79 100755
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_3.xml
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_3.xml
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/AndroidManifestParserTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/AndroidManifestParserTest.java
index 02725f4..02725f4 100644
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/AndroidManifestParserTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/AndroidManifestParserTest.java
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/SupportsScreensTest.java
index baf13b1..baf13b1 100644
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/SupportsScreensTest.java
diff --git a/sdkmanager/libs/sdkuilib/.classpath b/sdkmanager/libs/sdkuilib/.classpath
index b62b292..9080f57 100644
--- a/sdkmanager/libs/sdkuilib/.classpath
+++ b/sdkmanager/libs/sdkuilib/.classpath
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="tests"/>
+ <classpathentry excluding="**/Android.mk" kind="src" path="src"/>
+ <classpathentry excluding="**/Android.mk" kind="src" path="tests"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry combineaccessrules="false" kind="src" path="/common"/>
+ <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.core.prefs b/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..17cc168
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,64 @@
+#Wed Mar 16 15:10:22 PDT 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
diff --git a/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.ui.prefs b/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.ui.prefs
new file mode 100755
index 0000000..7471e0b
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,54 @@
+#Fri Apr 08 10:49:40 PDT 2011
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/sdkmanager/libs/sdkuilib/Android.mk b/sdkmanager/libs/sdkuilib/Android.mk
index 8e0bc23..740615e 100644
--- a/sdkmanager/libs/sdkuilib/Android.mk
+++ b/sdkmanager/libs/sdkuilib/Android.mk
@@ -1,4 +1,43 @@
-# Copyright 2008 The Android Open Source Project
#
-SDKUILIB_LOCAL_DIR := $(call my-dir)
-include $(SDKUILIB_LOCAL_DIR)/src/Android.mk
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+# IMPORTANT: if you add a new dependency here, please make sure
+# to also check the following file:
+# sdkmanager/app/etc/android.bat
+# (Note: there is no manifest.txt for sdkuilib.)
+LOCAL_JAVA_LIBRARIES := \
+ sdklib \
+ common \
+ androidprefs \
+ swtmenubar \
+ swt \
+ org.eclipse.jface_3.4.2.M20090107-0800 \
+ org.eclipse.equinox.common_3.4.0.v20080421-2006 \
+ org.eclipse.core.commands_3.4.0.I20080509-2000
+
+LOCAL_MODULE := sdkuilib
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/sdkmanager/libs/sdkuilib/NOTICE b/sdkmanager/libs/sdkuilib/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/NOTICE
@@ -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/sdkmanager/libs/sdkuilib/README b/sdkmanager/libs/sdkuilib/README
index d66b84a..dee4a24 100644
--- a/sdkmanager/libs/sdkuilib/README
+++ b/sdkmanager/libs/sdkuilib/README
@@ -1,11 +1,45 @@
-Using the Eclipse projects for ddmuilib.
+Using the Eclipse project SdkUiLib
+----------------------------------
-ddmuilib requires SWT to compile.
+1- sdkuilib requires SWT to compile.
-SWT is available in the depot under prebuild/<platform>/swt
+SWT is available in the tree under prebuild/<platform>/swt
Because the build path cannot contain relative path that are not inside the project directory,
the .classpath file references a user library called ANDROID_SWT.
-In order to compile the project, make a user library called ANDROID_SWT containing the jar
-available at prebuild/<platform>/swt. \ No newline at end of file
+In order to compile the project:
+- Open Preferences > Java > Build Path > User Libraries
+- Create a new user library named ANDROID_SWT
+- Add the following 4 JAR files:
+
+ - prebuilt/<platform>/swt/swt.jar
+ - prebuilt/common/eclipse/org.eclipse.core.commands_3.*.jar
+ - prebuilt/common/eclipse/org.eclipse.equinox.common_3.*.jar
+ - prebuilt/common/eclipse/org.eclipse.jface_3.*.jar
+
+
+2- sdkuilib also requires the compiled swtmenubar library.
+
+Build the swtmenubar library:
+$ cd $TOP (top of Android tree)
+$ . build/envsetup.sh && lunch sdk-eng
+$ sdk/eclipse/scripts/create_sdkman_symlinks.sh
+
+Define a classpath variable in Eclipse:
+- Open Preferences > Java > Build Path > Classpath Variables
+- Create a new classpath variable named ANDROID_OUT_FRAMEWORK
+- Set its folder value to <Android tree>/out/host/<platform>/framework
+- Create a new classpath variable named ANDROID_SRC
+- Set its folder value to <Android tree>
+
+You might need to clean the SdkUiLib project (Project > Clean...) after
+you add the new classpath variable, otherwise previous errors might not
+go away automatically.
+
+The ANDROID_SRC part should be optional. It allows you to have access to
+the SwtMenuBar generic parts from the Java editor.
+
+
+--
+EOF
diff --git a/sdkmanager/libs/sdkuilib/src/Android.mk b/sdkmanager/libs/sdkuilib/src/Android.mk
deleted file mode 100644
index 9aceba1..0000000
--- a/sdkmanager/libs/sdkuilib/src/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2008 The Android Open Source Project
-#
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_JAVA_RESOURCE_DIRS := .
-
-LOCAL_JAVA_LIBRARIES := \
- sdklib \
- common \
- androidprefs \
- swt \
- org.eclipse.jface_3.4.2.M20090107-0800 \
- org.eclipse.equinox.common_3.4.0.v20080421-2006 \
- org.eclipse.core.commands_3.4.0.I20080509-2000
-
-LOCAL_MODULE := sdkuilib
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AddonSitesDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AddonSitesDialog.java
new file mode 100755
index 0000000..9502099
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AddonSitesDialog.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2011 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.sdkuilib.internal.repository;
+
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.repository.SdkAddonSource;
+import com.android.sdklib.internal.repository.SdkSource;
+import com.android.sdklib.internal.repository.SdkSourceCategory;
+import com.android.sdklib.internal.repository.SdkSources;
+
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+
+import java.util.Arrays;
+
+public class AddonSitesDialog extends Dialog {
+
+ /**
+ * Min Y location for dialog. Need to deal with the menu bar on mac os.
+ */
+ private final static int MIN_Y = SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN ?
+ 20 : 0;
+
+
+ /** Last dialog size for this session. */
+ private static Point sLastSize;
+
+ private final UpdaterData mUpdaterData;
+ private boolean mChanged;
+
+ private Shell mShell;
+ private Table mTable;
+ private TableViewer mTableViewer;
+ private Button mButtonNew;
+ private Button mButtonDelete;
+ private Button mButtonClose;
+ private Label mlabel;
+ private Button mButtonEdit;
+ private TableColumn mColumnUrl;
+
+ /**
+ * Create the dialog.
+ *
+ * @param parent The parent's shell
+ */
+ public AddonSitesDialog(Shell parent, UpdaterData updaterData) {
+ super(parent, SWT.NONE);
+ mUpdaterData = updaterData;
+ setText("Add-on Sites");
+ }
+
+ /**
+ * Open the dialog.
+ *
+ * @return True if anything was changed.
+ */
+ public boolean open() {
+ createContents();
+ positionShell();
+ postCreate();
+ mShell.open();
+ mShell.layout();
+ Display display = getParent().getDisplay();
+ while (!mShell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+
+ return mChanged;
+ }
+
+ /**
+ * Create contents of the dialog.
+ */
+ private void createContents() {
+ mShell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE | SWT.APPLICATION_MODAL);
+ mShell.setMinimumSize(new Point(450, 300));
+ mShell.setSize(450, 300);
+ mShell.setText(getText());
+ GridLayout gl_shell = new GridLayout();
+ gl_shell.numColumns = 2;
+ mShell.setLayout(gl_shell);
+
+ mlabel = new Label(mShell, SWT.NONE);
+ mlabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ mlabel.setText(
+ "This dialog lets you manage the URLs of external add-on sites to be used.\n" +
+ "\n" +
+ "Add-on sites can provide new add-ons or \"user\" packages.\n" +
+ "They cannot provide standard Android platforms, docs or samples packages.\n" +
+ "Adding a URL here will not allow you to clone an official Android repository."
+ );
+
+ mTableViewer = new TableViewer(mShell, SWT.BORDER | SWT.FULL_SELECTION);
+ mTableViewer.addPostSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ on_TableViewer_selectionChanged(event);
+ }
+ });
+ mTable = mTableViewer.getTable();
+ mTable.setLinesVisible(false);
+ mTable.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseUp(MouseEvent e) {
+ on_Table_mouseUp(e);
+ }
+ });
+ mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 5));
+
+ TableViewerColumn tableViewerColumn = new TableViewerColumn(mTableViewer, SWT.NONE);
+ mColumnUrl = tableViewerColumn.getColumn();
+ mColumnUrl.setWidth(100);
+ mColumnUrl.setText("New Column");
+
+ mButtonNew = new Button(mShell, SWT.NONE);
+ mButtonNew.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ newOrEdit(false /*isEdit*/);
+ }
+ });
+ mButtonNew.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ mButtonNew.setText("New...");
+
+ mButtonEdit = new Button(mShell, SWT.NONE);
+ mButtonEdit.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ newOrEdit(true /*isEdit*/);
+ }
+ });
+ mButtonEdit.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ mButtonEdit.setText("Edit...");
+
+ mButtonDelete = new Button(mShell, SWT.NONE);
+ mButtonDelete.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ on_ButtonDelete_widgetSelected(e);
+ }
+ });
+ mButtonDelete.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ mButtonDelete.setText("Delete...");
+ new Label(mShell, SWT.NONE);
+
+ mButtonClose = new Button(mShell, SWT.NONE);
+ mButtonClose.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ on_ButtonClose_widgetSelected(e);
+ }
+ });
+ mButtonClose.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1));
+ mButtonClose.setText("Close");
+
+ adjustColumnsWidth(mTable, mColumnUrl);
+ }
+
+ /**
+ * Adds a listener to adjust the column width when the parent is resized.
+ */
+ private void adjustColumnsWidth(final Table table, final TableColumn column0) {
+ // Add a listener to resize the column to the full width of the table
+ table.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Rectangle r = table.getClientArea();
+ column0.setWidth(r.width * 100 / 100); // 100%
+ }
+ });
+ }
+
+ /**
+ * Centers the dialog in its parent shell.
+ */
+ private void positionShell() {
+ // Centers the dialog in its parent shell
+ Shell child = mShell;
+ Shell parent = getParent();
+ if (child != null && parent != null) {
+
+ // get the parent client area with a location relative to the display
+ Rectangle parentArea = parent.getClientArea();
+ Point parentLoc = parent.getLocation();
+ int px = parentLoc.x;
+ int py = parentLoc.y;
+ int pw = parentArea.width;
+ int ph = parentArea.height;
+
+ // Reuse the last size if there's one, otherwise use the default
+ Point childSize = sLastSize != null ? sLastSize : child.getSize();
+ int cw = childSize.x;
+ int ch = childSize.y;
+
+ int x = px + (pw - cw) / 2;
+ if (x < 0) x = 0;
+
+ int y = py + (ph - ch) / 2;
+ if (y < MIN_Y) y = MIN_Y;
+
+ child.setLocation(x, y);
+ child.setSize(cw, ch);
+ }
+ }
+
+ private void newOrEdit(final boolean isEdit) {
+ SdkSources sources = mUpdaterData.getSources();
+ final SdkSource[] knownSources = sources.getAllSources();
+ String title = isEdit ? "Edit Add-on Site URL" : "Add Add-on Site URL";
+ String msg = "Please enter the URL of the addon.xml:";
+ IStructuredSelection sel = (IStructuredSelection) mTableViewer.getSelection();
+ final String initialValue = !isEdit || sel.isEmpty() ? null :
+ sel.getFirstElement().toString();
+
+ if (isEdit && initialValue == null) {
+ // Edit with no actual value is not supposed to happen. Ignore this case.
+ return;
+ }
+
+ InputDialog dlg = new InputDialog(mShell, title, msg, initialValue, new IInputValidator() {
+ public String isValid(String newText) {
+
+ newText = newText == null ? null : newText.trim();
+
+ if (newText == null || newText.length() == 0) {
+ return "Error: URL field is empty. Please enter a URL.";
+ }
+
+ // A URL should have one of the following prefixes
+ if (!newText.startsWith("file://") && //$NON-NLS-1$
+ !newText.startsWith("ftp://") && //$NON-NLS-1$
+ !newText.startsWith("http://") && //$NON-NLS-1$
+ !newText.startsWith("https://")) { //$NON-NLS-1$
+ return "Error: The URL must start by one of file://, ftp://, http:// or https://";
+ }
+
+ if (isEdit && newText.equals(initialValue)) {
+ // Edited value hasn't changed. This isn't an error.
+ return null;
+ }
+
+ // Reject URLs that are already in the source list.
+ // URLs are generally case-insensitive (except for file:// where it all depends
+ // on the current OS so we'll ignore this case.)
+ for (SdkSource s : knownSources) {
+ if (newText.equalsIgnoreCase(s.getUrl())) {
+ return "Error: This site is already listed.";
+ }
+ }
+
+ return null;
+ }
+ });
+
+ if (dlg.open() == Window.OK) {
+ String url = dlg.getValue().trim();
+
+ if (!url.equals(initialValue)) {
+ if (isEdit && initialValue != null) {
+ // Remove the old value before we add the new one, which is we just
+ // asserted will be different.
+ for (SdkSource source : sources.getSources(SdkSourceCategory.USER_ADDONS)) {
+ if (initialValue.equals(source.getUrl())) {
+ sources.remove(source);
+ break;
+ }
+ }
+
+ }
+
+ // create the source, store it and update the list
+ SdkAddonSource newSource = new SdkAddonSource(url, null/*uiName*/);
+ sources.add(
+ SdkSourceCategory.USER_ADDONS,
+ newSource);
+ mChanged = true;
+ loadList();
+
+ // select the new source
+ IStructuredSelection newSel = new StructuredSelection(newSource);
+ mTableViewer.setSelection(newSel, true /*reveal*/);
+ }
+ }
+ }
+
+ private void on_ButtonDelete_widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) mTableViewer.getSelection();
+ String selectedUrl = sel.isEmpty() ? null : sel.getFirstElement().toString();
+
+ if (selectedUrl == null) {
+ return;
+ }
+
+ MessageBox mb = new MessageBox(mShell,
+ SWT.YES | SWT.NO | SWT.ICON_QUESTION | SWT.APPLICATION_MODAL);
+ mb.setText("Delete add-on site");
+ mb.setMessage(String.format("Do you want to delete the URL %1$s?", selectedUrl));
+ if (mb.open() == SWT.YES) {
+ SdkSources sources = mUpdaterData.getSources();
+ for (SdkSource source : sources.getSources(SdkSourceCategory.USER_ADDONS)) {
+ if (selectedUrl.equals(source.getUrl())) {
+ sources.remove(source);
+ mChanged = true;
+ loadList();
+ }
+ }
+ }
+ }
+
+ private void on_ButtonClose_widgetSelected(SelectionEvent e) {
+ mShell.close();
+ }
+
+ private void on_Table_mouseUp(MouseEvent e) {
+ Point p = new Point(e.x, e.y);
+ if (mTable.getItem(p) == null) {
+ mTable.deselectAll();
+ on_TableViewer_selectionChanged(null /*event*/);
+ }
+ }
+
+ private void on_TableViewer_selectionChanged(SelectionChangedEvent event) {
+ ISelection sel = mTableViewer.getSelection();
+ mButtonDelete.setEnabled(!sel.isEmpty());
+ mButtonEdit.setEnabled(!sel.isEmpty());
+ }
+
+ private void postCreate() {
+ // initialize the list
+ mTableViewer.setLabelProvider(new LabelProvider());
+ mTableViewer.setContentProvider(new SourcesContentProvider());
+ loadList();
+ }
+
+ private void loadList() {
+ if (mUpdaterData != null) {
+ SdkSource[] knownSources =
+ mUpdaterData.getSources().getSources(SdkSourceCategory.USER_ADDONS);
+ Arrays.sort(knownSources);
+
+ ISelection oldSelection = mTableViewer.getSelection();
+
+ mTableViewer.setInput(knownSources);
+ mTableViewer.refresh();
+ // initialize buttons' state that depend on the list
+ on_TableViewer_selectionChanged(null /*event*/);
+
+ if (oldSelection != null && !oldSelection.isEmpty()) {
+ mTableViewer.setSelection(oldSelection, true /*reveal*/);
+ }
+ }
+ }
+
+ private static class SourcesContentProvider implements IStructuredContentProvider {
+
+ public void dispose() {
+ // pass
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // pass
+ }
+
+ public Object[] getElements(Object inputElement) {
+ if (inputElement instanceof SdkSource[]) {
+ return (Object[]) inputElement;
+ } else {
+ return new Object[0];
+ }
+ }
+ }
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java
index 7594f16..7c78983 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java
@@ -17,7 +17,6 @@
package com.android.sdkuilib.internal.repository;
import com.android.prefs.AndroidLocation.AndroidLocationException;
-import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdkuilib.internal.widgets.AvdSelector;
import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
import com.android.sdkuilib.repository.ISdkChangeListener;
@@ -58,7 +57,7 @@ public class AvdManagerPage extends Composite implements ISdkChangeListener {
try {
label.setText(String.format(
"List of existing Android Virtual Devices located at %s",
- AvdManager.getBaseAvdFolder()));
+ mUpdaterData.getAvdManager().getBaseAvdFolder()));
} catch (AndroidLocationException e) {
label.setText(e.getMessage());
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java
new file mode 100755
index 0000000..fc59404
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 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.sdkuilib.internal.repository;
+
+
+
+/**
+ * Interface for lifecycle events of pages.
+ */
+interface IPageListener {
+
+ /**
+ * The page was just selected and brought to front.
+ */
+ public void onPageSelected();
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
index d579094..d2824ea 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
@@ -42,6 +42,9 @@ import org.eclipse.swt.widgets.TableColumn;
import java.io.File;
+/**
+ * Page that displays all locally installed packages from the current SDK.
+ */
public class LocalPackagesPage extends Composite implements ISdkChangeListener {
private final UpdaterData mUpdaterData;
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java
new file mode 100755
index 0000000..708c2d6
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java
@@ -0,0 +1,1441 @@
+/*
+ * Copyright (C) 2011 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.sdkuilib.internal.repository;
+
+import com.android.sdklib.internal.repository.Archive;
+import com.android.sdklib.internal.repository.IDescription;
+import com.android.sdklib.internal.repository.IPackageVersion;
+import com.android.sdklib.internal.repository.ITask;
+import com.android.sdklib.internal.repository.ITaskMonitor;
+import com.android.sdklib.internal.repository.Package;
+import com.android.sdklib.internal.repository.PlatformPackage;
+import com.android.sdklib.internal.repository.PlatformToolPackage;
+import com.android.sdklib.internal.repository.SdkSource;
+import com.android.sdklib.internal.repository.ToolPackage;
+import com.android.sdklib.internal.repository.Package.UpdateInfo;
+import com.android.sdkuilib.internal.repository.icons.ImageFactory;
+import com.android.sdkuilib.repository.ISdkChangeListener;
+import com.android.util.Pair;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ITableColorProvider;
+import org.eclipse.jface.viewers.ITableFontProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeColumnViewerLabelProvider;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+/**
+ * Page that displays both locally installed packages as well as all known
+ * remote available packages. This gives an overview of what is installed
+ * vs what is available and allows the user to update or install packages.
+ */
+public class PackagesPage extends Composite
+ implements ISdkChangeListener, IPageListener {
+
+ private static final String ICON_CAT_OTHER = "pkgcat_other_16.png"; //$NON-NLS-1$
+ private static final String ICON_CAT_PLATFORM = "pkgcat_16.png"; //$NON-NLS-1$
+ private static final String ICON_SORT_BY_SOURCE = "source_icon16.png"; //$NON-NLS-1$
+ private static final String ICON_SORT_BY_API = "platform_pkg_16.png"; //$NON-NLS-1$
+ private static final String ICON_PKG_NEW = "pkg_new_16.png"; //$NON-NLS-1$
+ private static final String ICON_PKG_UPDATE = "pkg_update_16.png"; //$NON-NLS-1$
+ private static final String ICON_PKG_INSTALLED = "pkg_installed_16.png"; //$NON-NLS-1$
+
+ enum MenuAction {
+ RELOAD (SWT.NONE, "Reload"),
+ SHOW_ADDON_SITES (SWT.NONE, "Manage Sources..."),
+ TOGGLE_SHOW_ARCHIVES (SWT.CHECK, "Show Archives"),
+ TOGGLE_SHOW_INSTALLED_PKG (SWT.CHECK, "Show Installed Packages"),
+ TOGGLE_SHOW_OBSOLETE_PKG (SWT.CHECK, "Show Obsolete Packages"),
+ TOGGLE_SHOW_UPDATE_NEW_PKG (SWT.CHECK, "Show Updates/New Packages"),
+ SORT_API_LEVEL (SWT.RADIO, "Sort by API Level"),
+ SORT_SOURCE (SWT.RADIO, "Sort by Source")
+ ;
+
+ private final int mMenuStyle;
+ private final String mMenuTitle;
+
+ MenuAction(int menuStyle, String menuTitle) {
+ mMenuStyle = menuStyle;
+ mMenuTitle = menuTitle;
+ }
+
+ public int getMenuStyle() {
+ return mMenuStyle;
+ }
+
+ public String getMenuTitle() {
+ return mMenuTitle;
+ }
+ };
+
+ private final Map<MenuAction, MenuItem> mMenuActions = new HashMap<MenuAction, MenuItem>();
+
+ private final List<PkgItem> mPackages = new ArrayList<PkgItem>();
+ private final List<PkgCategory> mCategories = new ArrayList<PkgCategory>();
+ private final UpdaterData mUpdaterData;
+
+ private boolean mDisplayArchives = false;
+
+ private Text mTextSdkOsPath;
+ private Button mCheckSortSource;
+ private Button mCheckSortApi;
+ private Button mCheckFilterObsolete;
+ private Button mCheckFilterInstalled;
+ private Button mCheckFilterNew;
+ private Composite mGroupOptions;
+ private Composite mGroupSdk;
+ private Group mGroupPackages;
+ private Button mButtonDelete;
+ private Button mButtonInstall;
+ private Tree mTree;
+ private CheckboxTreeViewer mTreeViewer;
+ private TreeViewerColumn mColumnName;
+ private TreeViewerColumn mColumnApi;
+ private TreeViewerColumn mColumnRevision;
+ private TreeViewerColumn mColumnStatus;
+ private Color mColorUpdate;
+ private Font mTreeFontItalic;
+ private TreeColumn mTreeColumnName;
+
+ public PackagesPage(Composite parent, UpdaterData updaterData) {
+ super(parent, SWT.NONE);
+
+ mUpdaterData = updaterData;
+ createContents(this);
+
+ postCreate(); //$hide$
+ }
+
+ public void onPageSelected() {
+ if (mPackages.isEmpty()) {
+ // Initialize the package list the first time the page is shown.
+ loadPackages();
+ }
+ }
+
+ private void createContents(Composite parent) {
+ GridLayout gridLayout = new GridLayout(2, false);
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ parent.setLayout(gridLayout);
+
+ mGroupSdk = new Composite(parent, SWT.NONE);
+ mGroupSdk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+ mGroupSdk.setLayout(new GridLayout(2, false));
+
+ Label label1 = new Label(mGroupSdk, SWT.NONE);
+ label1.setText("SDK Path:");
+
+ mTextSdkOsPath = new Text(mGroupSdk, SWT.NONE);
+ mTextSdkOsPath.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mTextSdkOsPath.setEnabled(false);
+
+ mGroupPackages = new Group(parent, SWT.NONE);
+ mGroupPackages.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+ mGroupPackages.setText("Packages");
+ mGroupPackages.setLayout(new GridLayout(1, false));
+
+ mTreeViewer = new CheckboxTreeViewer(mGroupPackages, SWT.BORDER);
+
+ mTreeViewer.addCheckStateListener(new ICheckStateListener() {
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ onTreeCheckStateChanged(event); //$hide$
+ }
+ });
+
+ mTree = mTreeViewer.getTree();
+ mTree.setLinesVisible(true);
+ mTree.setHeaderVisible(true);
+ mTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+
+ // column name icon is set in sortPackages() depending on the current filter type
+ // (e.g. API level or source)
+ mColumnName = new TreeViewerColumn(mTreeViewer, SWT.NONE);
+ mTreeColumnName = mColumnName.getColumn();
+ mTreeColumnName.setWidth(340);
+ mTreeColumnName.setText("Name");
+
+ mColumnApi = new TreeViewerColumn(mTreeViewer, SWT.NONE);
+ TreeColumn treeColumn2 = mColumnApi.getColumn();
+ treeColumn2.setAlignment(SWT.CENTER);
+ treeColumn2.setWidth(50);
+ treeColumn2.setText("API");
+
+ mColumnRevision = new TreeViewerColumn(mTreeViewer, SWT.NONE);
+ TreeColumn treeColumn3 = mColumnRevision.getColumn();
+ treeColumn3.setAlignment(SWT.CENTER);
+ treeColumn3.setWidth(50);
+ treeColumn3.setText("Rev.");
+ treeColumn3.setToolTipText("Revision currently installed");
+
+
+ mColumnStatus = new TreeViewerColumn(mTreeViewer, SWT.NONE);
+ TreeColumn treeColumn4 = mColumnStatus.getColumn();
+ treeColumn4.setAlignment(SWT.LEAD);
+ treeColumn4.setWidth(190);
+ treeColumn4.setText("Status");
+
+ mGroupOptions = new Composite(mGroupPackages, SWT.NONE);
+ mGroupOptions.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ GridLayout gl_GroupOptions = new GridLayout(6, false);
+ gl_GroupOptions.marginWidth = 0;
+ gl_GroupOptions.marginHeight = 0;
+ mGroupOptions.setLayout(gl_GroupOptions);
+
+ Label label3 = new Label(mGroupOptions, SWT.NONE);
+ label3.setText("Show:");
+
+ mCheckFilterNew = new Button(mGroupOptions, SWT.CHECK);
+ mCheckFilterNew.setToolTipText("Show Updates and New");
+ mCheckFilterNew.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ sortPackages(true /*updateButtons*/);
+ }
+ });
+ mCheckFilterNew.setSelection(true);
+ mCheckFilterNew.setText("Updates/New");
+
+ mCheckFilterInstalled = new Button(mGroupOptions, SWT.CHECK);
+ mCheckFilterInstalled.setToolTipText("Show Installed");
+ mCheckFilterInstalled.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ sortPackages(true /*updateButtons*/);
+ }
+ });
+ mCheckFilterInstalled.setSelection(true);
+ mCheckFilterInstalled.setText("Installed");
+
+ mCheckFilterObsolete = new Button(mGroupOptions, SWT.CHECK);
+ mCheckFilterObsolete.setToolTipText("Also show obsolete packages");
+ mCheckFilterObsolete.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ sortPackages(true /*updateButtons*/);
+ }
+ });
+ mCheckFilterObsolete.setSelection(false);
+ mCheckFilterObsolete.setText("Obsolete");
+
+ Label placeholder2 = new Label(mGroupOptions, SWT.NONE);
+ placeholder2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ mButtonInstall = new Button(mGroupOptions, SWT.NONE);
+ mButtonInstall.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mButtonInstall.setToolTipText("Install all the selected packages");
+ mButtonInstall.setText("Install Selected...");
+ mButtonInstall.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onButtonInstall(); //$hide$
+ }
+ });
+
+ Label label2 = new Label(mGroupOptions, SWT.NONE);
+ label2.setText("Sort by:");
+
+ mCheckSortApi = new Button(mGroupOptions, SWT.RADIO);
+ mCheckSortApi.setToolTipText("Sort by API level");
+ mCheckSortApi.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ sortPackages(true /*updateButtons*/);
+ // Reset the expanded state when changing sort algorithm
+ expandInitial(mCategories);
+ }
+ });
+ mCheckSortApi.setText("API level");
+ mCheckSortApi.setSelection(true);
+
+ mCheckSortSource = new Button(mGroupOptions, SWT.RADIO);
+ mCheckSortSource.setToolTipText("Sort by Source");
+ mCheckSortSource.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ sortPackages(true /*updateButtons*/);
+ // Reset the expanded state when changing sort algorithm
+ expandInitial(mCategories);
+ }
+ });
+ mCheckSortSource.setText("Source");
+
+ new Label(mGroupOptions, SWT.NONE);
+ new Label(mGroupOptions, SWT.NONE);
+
+ mButtonDelete = new Button(mGroupOptions, SWT.NONE);
+ mButtonDelete.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mButtonDelete.setToolTipText("Delete an installed package");
+ mButtonDelete.setText("Delete...");
+ mButtonDelete.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onButtonDelete(); //$hide$
+ }
+ });
+ }
+
+ private Image getImage(String filename) {
+ if (mUpdaterData != null) {
+ ImageFactory imgFactory = mUpdaterData.getImageFactory();
+ if (imgFactory != null) {
+ return imgFactory.getImageByName(filename);
+ }
+ }
+ return null;
+ }
+
+
+ // -- Start of internal part ----------
+ // Hide everything down-below from SWT designer
+ //$hide>>$
+
+
+ // --- menu interactions ---
+
+ public void registerMenuAction(final MenuAction action, MenuItem item) {
+ item.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Button button = null;
+
+ switch (action) {
+ case RELOAD:
+ loadPackages();
+ break;
+ case SHOW_ADDON_SITES:
+ AddonSitesDialog d = new AddonSitesDialog(getShell(), mUpdaterData);
+ if (d.open()) {
+ loadPackages();
+ }
+ break;
+ case TOGGLE_SHOW_ARCHIVES:
+ mDisplayArchives = !mDisplayArchives;
+ sortPackages(true /*updateButtons*/);
+ break;
+ case TOGGLE_SHOW_INSTALLED_PKG:
+ button = mCheckFilterInstalled;
+ break;
+ case TOGGLE_SHOW_OBSOLETE_PKG:
+ button = mCheckFilterObsolete;
+ break;
+ case TOGGLE_SHOW_UPDATE_NEW_PKG:
+ button = mCheckFilterNew;
+ break;
+ case SORT_API_LEVEL:
+ button = mCheckSortApi;
+ break;
+ case SORT_SOURCE:
+ button = mCheckSortSource;
+ break;
+ }
+
+ if (button != null && !button.isDisposed()) {
+ // Toggle this button (radio or checkbox)
+
+ boolean value = button.getSelection();
+
+ // SWT doesn't automatically switch radio buttons when using the
+ // Widget#setSelection method, so we'll do it here manually.
+ if (!value && (button.getStyle() & SWT.RADIO) != 0) {
+ // we'll be selecting this radio button, so deselect all ther other ones
+ // in the parent group.
+ for (Control child : button.getParent().getChildren()) {
+ if (child instanceof Button &&
+ child != button &&
+ (child.getStyle() & SWT.RADIO) != 0) {
+ ((Button) child).setSelection(value);
+ }
+ }
+ }
+
+ button.setSelection(!value);
+
+ // SWT doesn't actually invoke the listeners when using Widget#setSelection
+ // so let's run the actual action.
+ button.notifyListeners(SWT.Selection, new Event());
+ }
+
+ updateMenuCheckmarks();
+ }
+ });
+
+ mMenuActions.put(action, item);
+ }
+
+ // --- internal methods ---
+
+ private void updateMenuCheckmarks() {
+
+ for (Entry<MenuAction, MenuItem> entry : mMenuActions.entrySet()) {
+ MenuAction action = entry.getKey();
+ MenuItem item = entry.getValue();
+
+ if (action.getMenuStyle() == SWT.NONE) {
+ continue;
+ }
+
+ boolean value = false;
+ Button button = null;
+
+ switch (action) {
+ case TOGGLE_SHOW_ARCHIVES:
+ value = mDisplayArchives;
+ break;
+ case TOGGLE_SHOW_INSTALLED_PKG:
+ button = mCheckFilterInstalled;
+ break;
+ case TOGGLE_SHOW_OBSOLETE_PKG:
+ button = mCheckFilterObsolete;
+ break;
+ case TOGGLE_SHOW_UPDATE_NEW_PKG:
+ button = mCheckFilterNew;
+ break;
+ case SORT_API_LEVEL:
+ button = mCheckSortApi;
+ break;
+ case SORT_SOURCE:
+ button = mCheckSortSource;
+ break;
+ }
+
+ if (button != null && !button.isDisposed()) {
+ value = button.getSelection();
+ }
+
+ item.setSelection(value);
+ }
+
+ }
+
+ private void postCreate() {
+ if (mUpdaterData != null) {
+ mTextSdkOsPath.setText(mUpdaterData.getOsSdkRoot());
+ }
+
+ mTreeViewer.setContentProvider(new PkgContentProvider());
+
+ mColumnApi.setLabelProvider(
+ new TreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnApi)));
+ mColumnName.setLabelProvider(
+ new TreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnName)));
+ mColumnStatus.setLabelProvider(
+ new TreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnStatus)));
+ mColumnRevision.setLabelProvider(
+ new TreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnRevision)));
+
+ FontData fontData = mTree.getFont().getFontData()[0];
+ fontData.setStyle(SWT.ITALIC);
+ mTreeFontItalic = new Font(mTree.getDisplay(), fontData);
+
+ mColorUpdate = new Color(mTree.getDisplay(), 0xff, 0xff, 0xcc);
+
+ mTree.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ mTreeFontItalic.dispose();
+ mColorUpdate.dispose();
+ mTreeFontItalic = null;
+ mColorUpdate = null;
+ }
+ });
+ }
+
+ private void loadPackages() {
+ if (mUpdaterData == null) {
+ return;
+ }
+
+ try {
+ enableUi(mGroupPackages, false);
+
+ boolean firstLoad = mPackages.size() == 0;
+ mPackages.clear();
+
+ // get local packages
+ for (Package pkg : mUpdaterData.getInstalledPackages()) {
+ PkgItem pi = new PkgItem(pkg, PkgState.INSTALLED);
+ mPackages.add(pi);
+ }
+
+ // get remote packages
+ final boolean forceHttp = mUpdaterData.getSettingsController().getForceHttp();
+ mUpdaterData.loadRemoteAddonsList();
+ mUpdaterData.getTaskFactory().start("Loading Sources", new ITask() {
+ public void run(ITaskMonitor monitor) {
+ SdkSource[] sources = mUpdaterData.getSources().getAllSources();
+ for (SdkSource source : sources) {
+ Package[] pkgs = source.getPackages();
+ if (pkgs == null) {
+ source.load(monitor, forceHttp);
+ pkgs = source.getPackages();
+ }
+ if (pkgs == null) {
+ continue;
+ }
+
+ nextPkg: for(Package pkg : pkgs) {
+ boolean isUpdate = false;
+ for (PkgItem pi: mPackages) {
+ if (pi.isSameAs(pkg)) {
+ continue nextPkg;
+ }
+ if (pi.isUpdatedBy(pkg)) {
+ isUpdate = true;
+ break;
+ }
+ }
+
+ if (!isUpdate) {
+ PkgItem pi = new PkgItem(pkg, PkgState.NEW);
+ mPackages.add(pi);
+ }
+ }
+
+ // Dynamically update the table while we load after each source.
+ // Since the official Android source gets loaded first, it makes the
+ // window look non-empty a lot sooner.
+ mGroupPackages.getDisplay().syncExec(new Runnable() {
+ public void run() {
+ sortPackages(true /*updateButtons*/);
+ }
+ });
+ }
+
+ monitor.setDescription("Done loading %1$d packages from %2$d sources",
+ mPackages.size(),
+ sources.length);
+ }
+ });
+
+ if (firstLoad) {
+ // set the initial expanded state
+ expandInitial(mCategories);
+ }
+
+ } finally {
+ enableUi(mGroupPackages, true);
+ updateButtonsState();
+ updateMenuCheckmarks();
+ }
+ }
+
+ private void enableUi(Composite root, boolean enabled) {
+ root.setEnabled(enabled);
+ for (Control child : root.getChildren()) {
+ if (child instanceof Composite) {
+ enableUi((Composite) child, enabled);
+ } else {
+ child.setEnabled(enabled);
+ }
+ }
+ }
+
+ private void sortPackages(boolean updateButtons) {
+ if (mCheckSortApi != null && !mCheckSortApi.isDisposed() && mCheckSortApi.getSelection()) {
+ sortByApiLevel();
+ } else {
+ sortBySource();
+ }
+ if (updateButtons) {
+ updateButtonsState();
+ updateMenuCheckmarks();
+ }
+ }
+
+ /**
+ * Recompute the tree by sorting all the packages by API.
+ * This does an update in-place of the mCategories list so that the table
+ * can preserve its state (checked / expanded / selected) properly.
+ */
+ private void sortByApiLevel() {
+
+ ImageFactory imgFactory = mUpdaterData.getImageFactory();
+
+ if (!mTreeColumnName.isDisposed()) {
+ mTreeColumnName.setImage(getImage(ICON_SORT_BY_API));
+ }
+
+ // keep a map of the initial state so that we can detect which items or categories are
+ // no longer being used, so that we can removed them at the end of the in-place update.
+ final Map<Integer, Pair<PkgCategory, HashSet<PkgItem>> > unusedItemsMap =
+ new HashMap<Integer, Pair<PkgCategory, HashSet<PkgItem>> >();
+ final Set<PkgCategory> unusedCatSet = new HashSet<PkgCategory>();
+
+ // get existing categories
+ for (PkgCategory cat : mCategories) {
+ unusedCatSet.add(cat);
+ unusedItemsMap.put(cat.getKey(), Pair.of(cat, new HashSet<PkgItem>(cat.getItems())));
+ }
+
+ // always add the tools & extras categories, even if empty (unlikely anyway)
+ if (!unusedItemsMap.containsKey(PkgCategory.KEY_TOOLS)) {
+ PkgCategory cat = new PkgCategory(
+ PkgCategory.KEY_TOOLS,
+ "Tools",
+ imgFactory.getImageByName(ICON_CAT_OTHER));
+ unusedItemsMap.put(PkgCategory.KEY_TOOLS, Pair.of(cat, new HashSet<PkgItem>()));
+ mCategories.add(cat);
+ }
+
+ if (!unusedItemsMap.containsKey(PkgCategory.KEY_EXTRA)) {
+ PkgCategory cat = new PkgCategory(
+ PkgCategory.KEY_EXTRA,
+ "Add-ons & Extras",
+ imgFactory.getImageByName(ICON_CAT_OTHER));
+ unusedItemsMap.put(PkgCategory.KEY_EXTRA, Pair.of(cat, new HashSet<PkgItem>()));
+ mCategories.add(cat);
+ }
+
+ for (PkgItem item : mPackages) {
+ if (!keepItem(item)) {
+ continue;
+ }
+
+ int apiKey = item.getApi();
+
+ if (apiKey < 1) {
+ Package p = item.getPackage();
+ if (p instanceof ToolPackage || p instanceof PlatformToolPackage) {
+ apiKey = PkgCategory.KEY_TOOLS;
+ } else {
+ apiKey = PkgCategory.KEY_EXTRA;
+ }
+ }
+
+ Pair<PkgCategory, HashSet<PkgItem>> mapEntry = unusedItemsMap.get(apiKey);
+
+ if (mapEntry == null) {
+ // This is a new category. Create it and add it to the map.
+ // We need a label for the category. Use null right now and set it later.
+ PkgCategory cat = new PkgCategory(
+ apiKey,
+ null /*label*/,
+ imgFactory.getImageByName(ICON_CAT_PLATFORM));
+ mapEntry = Pair.of(cat, new HashSet<PkgItem>());
+ unusedItemsMap.put(apiKey, mapEntry);
+ mCategories.add(0, cat);
+ }
+ PkgCategory cat = mapEntry.getFirst();
+ assert cat != null;
+ unusedCatSet.remove(cat);
+
+ HashSet<PkgItem> unusedItemsSet = mapEntry.getSecond();
+ unusedItemsSet.remove(item);
+ if (!cat.getItems().contains(item)) {
+ cat.getItems().add(item);
+ }
+
+ if (apiKey != -1 && cat.getLabel() == null) {
+ // Check whether we can get the actual platform version name (e.g. "1.5")
+ // from the first Platform package we find in this category.
+ Package p = item.getPackage();
+ if (p instanceof PlatformPackage) {
+ String vn = ((PlatformPackage) p).getVersionName();
+ String name = String.format("Android %1$s (API %2$d)", vn, apiKey);
+ cat.setLabel(name);
+ }
+ }
+ }
+
+ for (Iterator<PkgCategory> iterCat = mCategories.iterator(); iterCat.hasNext(); ) {
+ PkgCategory cat = iterCat.next();
+
+ // Remove any unused categories.
+ if (unusedCatSet.contains(cat)) {
+ iterCat.remove();
+ continue;
+ }
+
+ // Remove any unused items in the category.
+ Integer apikey = cat.getKey();
+ Pair<PkgCategory, HashSet<PkgItem>> mapEntry = unusedItemsMap.get(apikey);
+ assert mapEntry != null;
+ HashSet<PkgItem> unusedItems = mapEntry.getSecond();
+ for (Iterator<PkgItem> iterItem = cat.getItems().iterator(); iterItem.hasNext(); ) {
+ PkgItem item = iterItem.next();
+ if (unusedItems.contains(item)) {
+ iterItem.remove();
+ }
+ }
+
+ // Sort the items
+ Collections.sort(cat.getItems());
+
+ // Fix the category name for any API where we might not have found a platform package.
+ if (cat.getLabel() == null) {
+ int api = cat.getKey().intValue();
+ String name = String.format("API %1$d", api);
+ cat.setLabel(name);
+ }
+ }
+
+ // Sort the categories list in decreasing order
+ Collections.sort(mCategories, new Comparator<PkgCategory>() {
+ public int compare(PkgCategory cat1, PkgCategory cat2) {
+ // compare in descending order (o2-o1)
+ return cat2.getKey().compareTo(cat1.getKey());
+ }
+ });
+
+ if (mTreeViewer.getInput() != mCategories) {
+ // set initial input
+ mTreeViewer.setInput(mCategories);
+ } else {
+ // refresh existing, which preserves the expanded state, the selection
+ // and the checked state.
+ mTreeViewer.refresh();
+ }
+ }
+
+ /**
+ * Recompute the tree by sorting all packages by source.
+ */
+ private void sortBySource() {
+
+ if (!mTreeColumnName.isDisposed()) {
+ mTreeColumnName.setImage(getImage(ICON_SORT_BY_SOURCE));
+ }
+
+ mCategories.clear();
+
+ Set<SdkSource> sourceSet = new HashSet<SdkSource>();
+ for (PkgItem item : mPackages) {
+ if (keepItem(item)) {
+ sourceSet.add(item.getSource());
+ }
+ }
+
+ SdkSource[] sources = sourceSet.toArray(new SdkSource[sourceSet.size()]);
+ Arrays.sort(sources, new Comparator<SdkSource>() {
+ public int compare(SdkSource o1, SdkSource o2) {
+ if (o1 == o2) {
+ return 0;
+ } else if (o1 == null && o2 != null) {
+ return -1;
+ } else if (o1 != null && o2 == null) {
+ return 1;
+ }
+ assert o1 != null;
+ return o1.toString().compareTo(o2.toString());
+ }
+ });
+
+ for (SdkSource source : sources) {
+ Object key = source != null ? source : "Installed Packages";
+ Object iconRef = source != null ? source :
+ mUpdaterData.getImageFactory().getImageByName(ICON_PKG_INSTALLED);
+
+ PkgCategory cat = new PkgCategory(
+ key.hashCode(),
+ key.toString(),
+ iconRef);
+
+ for (PkgItem item : mPackages) {
+ if (item.getSource() == source) {
+ cat.getItems().add(item);
+ }
+ }
+
+ mCategories.add(cat);
+ }
+
+ // We don't support in-place incremental updates so the table gets reset
+ // each time we load when sorted by source.
+ mTreeViewer.setInput(mCategories);
+ }
+
+ /**
+ * Decide whether to keep an item in the current tree based on user-choosen filter options.
+ */
+ private boolean keepItem(PkgItem item) {
+ if (!mCheckFilterObsolete.getSelection()) {
+ if (item.isObsolete()) {
+ return false;
+ }
+ }
+
+ if (!mCheckFilterInstalled.getSelection()) {
+ if (item.getState() == PkgState.INSTALLED) {
+ return false;
+ }
+ }
+
+ if (!mCheckFilterNew.getSelection()) {
+ if (item.getState() == PkgState.NEW ||
+ item.getState() == PkgState.HAS_UPDATE) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Performs the initial expansion of the tree. The expands categories that contains
+ * at least one installed item and collapses the ones with nothing installed.
+ */
+ private void expandInitial(Object elem) {
+ mTreeViewer.setExpandedState(elem, true);
+ for (Object pkg :
+ ((ITreeContentProvider) mTreeViewer.getContentProvider()).getChildren(elem)) {
+ if (pkg instanceof PkgCategory) {
+ PkgCategory cat = (PkgCategory) pkg;
+ for (PkgItem item : cat.getItems()) {
+ if (item.getState() == PkgState.INSTALLED
+ || item.getState() == PkgState.HAS_UPDATE) {
+ expandInitial(pkg);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Handle checking and unchecking of the tree items.
+ *
+ * When unchecking, all sub-tree items checkboxes are cleared too.
+ * When checking a source, all of its packages are checked too.
+ * When checking a package, only its compatible archives are checked.
+ */
+ private void onTreeCheckStateChanged(CheckStateChangedEvent event) {
+ boolean b = event.getChecked();
+ Object elem = event.getElement();
+
+ assert event.getSource() == mTreeViewer;
+
+ // when deselecting, we just deselect all children too
+ if (b == false) {
+ mTreeViewer.setSubtreeChecked(elem, b);
+ updateButtonsState();
+ return;
+ }
+
+ ITreeContentProvider provider = (ITreeContentProvider) mTreeViewer.getContentProvider();
+
+ // When selecting, we want to only select compatible archives and expand the super nodes.
+ checkExpandItem(elem, provider);
+
+ updateButtonsState();
+ }
+
+ private void checkExpandItem(Object elem, ITreeContentProvider provider) {
+ if (elem instanceof PkgCategory || elem instanceof PkgItem) {
+ mTreeViewer.setExpandedState(elem, true);
+ for (Object pkg : provider.getChildren(elem)) {
+ mTreeViewer.setChecked(pkg, true);
+ checkExpandItem(pkg, provider);
+ }
+ } else if (elem instanceof Package) {
+ selectCompatibleArchives(elem, provider);
+ }
+ }
+
+ private void selectCompatibleArchives(Object pkg, ITreeContentProvider provider) {
+ for (Object archive : provider.getChildren(pkg)) {
+ if (archive instanceof Archive) {
+ mTreeViewer.setChecked(archive, ((Archive) archive).isCompatible());
+ }
+ }
+ }
+
+ private void updateButtonsState() {
+ boolean canInstall = false;
+
+ if (mDisplayArchives) {
+ // In detail mode, we display archives so we can install if at
+ // least one archive is selected.
+
+ Object[] checked = mTreeViewer.getCheckedElements();
+ if (checked != null) {
+ for (Object c : checked) {
+ if (c instanceof Archive) {
+ if (((Archive) c).isCompatible()) {
+ canInstall = true;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ // In non-detail mode, we need to check if there are any packages
+ // or pkgitems selected with at least one compatible archive to be
+ // installed.
+
+ Object[] checked = mTreeViewer.getCheckedElements();
+ if (checked != null) {
+ for (Object c : checked) {
+ if (c instanceof Package) {
+ // This is an update package
+ if (((Package) c).hasCompatibleArchive()) {
+ canInstall = true;
+ break;
+ }
+ } else if (c instanceof PkgItem) {
+ if (((PkgItem) c).getPackage().hasCompatibleArchive()) {
+ canInstall = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ mButtonInstall.setEnabled(canInstall);
+
+ // We can only delete local archives
+ boolean canDelete = false;
+ Object[] checked = mTreeViewer.getCheckedElements();
+ if (checked != null) {
+ for (Object c : checked) {
+ if (c instanceof PkgItem) {
+ if (((PkgItem) c).getState() == PkgState.INSTALLED) {
+ canDelete = true;
+ break;
+ }
+ }
+ }
+ }
+
+ mButtonDelete.setEnabled(canDelete);
+ }
+
+ private void onButtonInstall() {
+ ArrayList<Archive> archives = new ArrayList<Archive>();
+
+ if (mDisplayArchives) {
+ // In detail mode, we display archives so we can install only the
+ // archives that are actually selected.
+
+ Object[] checked = mTreeViewer.getCheckedElements();
+ if (checked != null) {
+ for (Object c : checked) {
+ if (c instanceof Archive) {
+ if (((Archive) c).isCompatible()) {
+ archives.add((Archive) c);
+ }
+ }
+ }
+ }
+ } else {
+ // In non-detail mode, we install all the compatible archives
+ // found in the selected pkg items or update packages.
+
+ Object[] checked = mTreeViewer.getCheckedElements();
+ if (checked != null) {
+ for (Object c : checked) {
+ Package p = null;
+ if (c instanceof Package) {
+ // This is an update package
+ p = (Package) c;
+ } else if (c instanceof PkgItem) {
+ p = ((PkgItem) c).getPackage();
+ }
+ if (p != null) {
+ for (Archive a : p.getArchives()) {
+ if (a.isCompatible()) {
+ archives.add(a);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (mUpdaterData != null) {
+ try {
+ enableUi(mGroupPackages, false);
+
+ mUpdaterData.updateOrInstallAll_WithGUI(
+ archives,
+ mCheckFilterObsolete.getSelection() /* includeObsoletes */);
+ } finally {
+ // loadPackages will also re-enable the UI
+ loadPackages();
+ }
+ }
+ }
+
+ private void onButtonDelete() {
+ // Find selected local packages to be delete
+ Object[] checked = mTreeViewer.getCheckedElements();
+ if (checked == null) {
+ // This should not happen since the button should be disabled
+ return;
+ }
+
+ String title = "Delete SDK Package";
+ String msg = "Are you sure you want to delete:";
+ final List<Archive> archives = new ArrayList<Archive>();
+
+ for (Object c : checked) {
+ if (c instanceof PkgItem && ((PkgItem) c).getState() == PkgState.INSTALLED) {
+ Package p = ((PkgItem) c).getPackage();
+
+ Archive[] as = p.getArchives();
+ if (as.length == 1 && as[0] != null && as[0].isLocal()) {
+ Archive archive = as[0];
+ String osPath = archive.getLocalOsPath();
+
+ File dir = new File(osPath);
+ if (dir.isDirectory()) {
+ msg += "\n - " + p.getShortDescription();
+ archives.add(archive);
+ }
+ }
+ }
+ }
+
+ if (!archives.isEmpty()) {
+ msg += "\n" + "This cannot be undone.";
+ if (MessageDialog.openQuestion(getShell(), title, msg)) {
+ try {
+ enableUi(mGroupPackages, false);
+
+ mUpdaterData.getTaskFactory().start("Loading Sources", new ITask() {
+ public void run(ITaskMonitor monitor) {
+ monitor.setProgressMax(archives.size() + 1);
+ for (Archive a : archives) {
+ monitor.setDescription("Deleting '%1$s' (%2$s)",
+ a.getParentPackage().getShortDescription(),
+ a.getLocalOsPath());
+ a.deleteLocal();
+ monitor.incProgress(1);
+ if (monitor.isCancelRequested()) {
+ break;
+ }
+ }
+
+ monitor.incProgress(1);
+ monitor.setDescription("Done");
+ }
+ });
+ } finally {
+ // loadPackages will also re-enable the UI
+ loadPackages();
+ }
+ }
+ }
+ }
+
+ // ----------------------
+
+ public class PkgCellLabelProvider extends ColumnLabelProvider
+ implements ITableFontProvider, ITableColorProvider {
+
+ private final TreeViewerColumn mColumn;
+
+ public PkgCellLabelProvider(TreeViewerColumn column) {
+ super();
+ mColumn = column;
+ }
+
+ @Override
+ public String getText(Object element) {
+
+ if (mColumn == mColumnName) {
+
+ if (element instanceof PkgCategory) {
+ return ((PkgCategory) element).getLabel();
+ } else if (element instanceof PkgItem) {
+ return ((PkgItem) element).getName();
+ } else if (element instanceof IDescription) {
+ return ((IDescription) element).getShortDescription();
+ }
+
+ } else if (mColumn == mColumnApi) {
+
+ int api = -1;
+ if (element instanceof PkgItem) {
+ api = ((PkgItem) element).getApi();
+ }
+ if (api >= 1) {
+ return Integer.toString(api);
+ }
+
+ } else if (mColumn == mColumnRevision) {
+
+ if (element instanceof PkgItem) {
+ PkgItem pkg = (PkgItem) element;
+
+ if (pkg.getState() == PkgState.INSTALLED ||
+ pkg.getState() == PkgState.HAS_UPDATE) {
+ return Integer.toString(pkg.getRevision());
+ }
+ }
+
+ } else if (mColumn == mColumnStatus) {
+
+ if (element instanceof PkgItem) {
+ PkgItem pkg = (PkgItem) element;
+
+ switch(pkg.getState()) {
+ case INSTALLED:
+ return "Installed";
+ case HAS_UPDATE:
+ return "Update available";
+ case NEW:
+ return "Not installed. New revision " + Integer.toString(pkg.getRevision());
+ }
+ return pkg.getState().toString();
+
+ } else if (element instanceof Package) {
+ // This is an update package.
+ return "New revision " + Integer.toString(((Package) element).getRevision());
+ }
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ ImageFactory imgFactory = mUpdaterData.getImageFactory();
+
+ if (imgFactory != null) {
+ if (mColumn == mColumnName) {
+ if (element instanceof PkgCategory) {
+ return imgFactory.getImageForObject(((PkgCategory) element).getIconRef());
+ } else if (element instanceof PkgItem) {
+ return imgFactory.getImageForObject(((PkgItem) element).getPackage());
+ }
+ return imgFactory.getImageForObject(element);
+
+ } else if (mColumn == mColumnStatus && element instanceof PkgItem) {
+ switch(((PkgItem) element).getState()) {
+ case INSTALLED:
+ return imgFactory.getImageByName(ICON_PKG_INSTALLED);
+ case HAS_UPDATE:
+ return imgFactory.getImageByName(ICON_PKG_UPDATE);
+ case NEW:
+ return imgFactory.getImageByName(ICON_PKG_NEW);
+ }
+ }
+ }
+ return super.getImage(element);
+ }
+
+ // -- ITableFontProvider
+
+ public Font getFont(Object element, int columnIndex) {
+ if (element instanceof PkgItem) {
+ if (((PkgItem) element).getState() == PkgState.NEW) {
+ return mTreeFontItalic;
+ }
+ } else if (element instanceof Package) {
+ // update package
+ return mTreeFontItalic;
+ }
+ return super.getFont(element);
+ }
+
+ // -- ITableColorProvider
+
+ public Color getBackground(Object element, int columnIndex) {
+ if (element instanceof PkgItem) {
+ if (((PkgItem) element).getState() == PkgState.HAS_UPDATE) {
+ return mColorUpdate;
+ }
+ }
+ return null;
+ }
+
+ public Color getForeground(Object element, int columnIndex) {
+ // Not used
+ return null;
+ }
+ }
+
+ private class PkgContentProvider implements ITreeContentProvider {
+
+ public Object[] getChildren(Object parentElement) {
+
+ if (parentElement instanceof ArrayList<?>) {
+ return ((ArrayList<?>) parentElement).toArray();
+
+ } else if (parentElement instanceof PkgCategory) {
+ return ((PkgCategory) parentElement).getItems().toArray();
+
+ } else if (parentElement instanceof PkgItem) {
+ List<Package> pkgs = ((PkgItem) parentElement).getUpdatePkgs();
+ if (pkgs != null) {
+ return pkgs.toArray();
+ }
+
+ if (mDisplayArchives) {
+ return ((PkgItem) parentElement).getArchives();
+ }
+
+ } else if (parentElement instanceof Package) {
+ if (mDisplayArchives) {
+ return ((Package) parentElement).getArchives();
+ }
+
+ }
+
+ return new Object[0];
+ }
+
+ public Object getParent(Object element) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean hasChildren(Object parentElement) {
+ if (parentElement instanceof ArrayList<?>) {
+ return true;
+
+ } else if (parentElement instanceof PkgCategory) {
+ return true;
+
+ } else if (parentElement instanceof PkgItem) {
+ List<Package> pkgs = ((PkgItem) parentElement).getUpdatePkgs();
+ if (pkgs != null) {
+ return !pkgs.isEmpty();
+ }
+
+ if (mDisplayArchives) {
+ Archive[] archives = ((PkgItem) parentElement).getArchives();
+ return archives.length > 0;
+ }
+ } else if (parentElement instanceof Package) {
+ if (mDisplayArchives) {
+ return ((Package) parentElement).getArchives().length > 0;
+ }
+ }
+
+ return false;
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return getChildren(inputElement);
+ }
+
+ public void dispose() {
+ // unused
+
+ }
+
+ public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
+ // unused
+ }
+ }
+
+ private static class PkgCategory {
+ private final Integer mKey;
+ private final Object mIconRef;
+ private final List<PkgItem> mItems = new ArrayList<PkgItem>();
+ private String mLabel;
+
+ // When storing by API, key is the API level (>=1), except 0 is tools and 1 is extra/addons.
+ // When sorting by Source, key is the hash of the source's name.
+ public final static Integer KEY_TOOLS = Integer.valueOf(0);
+ public final static Integer KEY_EXTRA = Integer.valueOf(-1);
+
+ public PkgCategory(Integer key, String label, Object iconRef) {
+ mKey = key;
+ mLabel = label;
+ mIconRef = iconRef;
+ }
+
+ public Integer getKey() {
+ return mKey;
+ }
+
+ public String getLabel() {
+ return mLabel;
+ }
+
+ public void setLabel(String label) {
+ mLabel = label;
+ }
+
+ public Object getIconRef() {
+ return mIconRef;
+ }
+
+ public List<PkgItem> getItems() {
+ return mItems;
+ }
+ }
+
+ public enum PkgState {
+ /**
+ * Package is locally installed and has no update available on remote sites.
+ */
+ INSTALLED,
+
+ /**
+ * Package is installed and has an update available.
+ * In this case, {@link PkgItem#getUpdatePkgs()} provides the list of 1 or more
+ * packages that can update this {@link PkgItem}.
+ */
+ HAS_UPDATE,
+
+ /**
+ * There's a new package available on the remote site that isn't
+ * installed locally.
+ */
+ NEW
+ }
+
+ public static class PkgItem implements Comparable<PkgItem> {
+ private final Package mPkg;
+ private PkgState mState;
+ private List<Package> mUpdatePkgs;
+
+ public PkgItem(Package pkg, PkgState state) {
+ mPkg = pkg;
+ mState = state;
+ assert mPkg != null;
+ }
+
+ public boolean isObsolete() {
+ return mPkg.isObsolete();
+ }
+
+ public boolean isSameAs(Package pkg) {
+ return mPkg.canBeUpdatedBy(pkg) == UpdateInfo.NOT_UPDATE;
+ }
+
+ /**
+ * Check whether the 'pkg' argument updates this package.
+ * If it does, record it as a sub-package.
+ * Returns true if it was recorded as an update, false otherwise.
+ */
+ public boolean isUpdatedBy(Package pkg) {
+ if (mPkg.canBeUpdatedBy(pkg) == UpdateInfo.UPDATE) {
+ if (mUpdatePkgs == null) {
+ mUpdatePkgs = new ArrayList<Package>();
+ }
+ mUpdatePkgs.add(pkg);
+ mState = PkgState.HAS_UPDATE;
+ return true;
+ }
+
+ return false;
+ }
+
+ public String getName() {
+ return mPkg.getListDescription();
+ }
+
+ public int getRevision() {
+ return mPkg.getRevision();
+ }
+
+ public String getDescription() {
+ return mPkg.getDescription();
+ }
+
+ public Package getPackage() {
+ return mPkg;
+ }
+
+ public PkgState getState() {
+ return mState;
+ }
+
+ public SdkSource getSource() {
+ if (mState == PkgState.NEW) {
+ return mPkg.getParentSource();
+ } else {
+ return null;
+ }
+ }
+
+ public int getApi() {
+ return mPkg instanceof IPackageVersion ?
+ ((IPackageVersion) mPkg).getVersion().getApiLevel() :
+ -1;
+ }
+
+ public List<Package> getUpdatePkgs() {
+ return mUpdatePkgs;
+ }
+
+ public Archive[] getArchives() {
+ return mPkg.getArchives();
+ }
+
+ public int compareTo(PkgItem pkg) {
+ return getPackage().compareTo(pkg.getPackage());
+ }
+ }
+
+
+
+ // --- Implementation of ISdkChangeListener ---
+
+ public void onSdkLoaded() {
+ onSdkReload();
+ }
+
+ public void onSdkReload() {
+ loadPackages();
+ }
+
+ public void preInstallHook() {
+ // nothing to be done for now.
+ }
+
+ public void postInstallHook() {
+ // nothing to be done for now.
+ }
+
+ // --- End of hiding from SWT Designer ---
+ //$hide<<$
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
index 6fbf060..0a70162 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
@@ -1,495 +1,497 @@
-/*
- * 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.sdkuilib.internal.repository;
-
-
-import com.android.sdklib.internal.repository.Archive;
-import com.android.sdklib.internal.repository.IDescription;
-import com.android.sdklib.internal.repository.Package;
-import com.android.sdklib.internal.repository.SdkAddonSource;
-import com.android.sdklib.internal.repository.SdkSource;
-import com.android.sdklib.internal.repository.SdkSourceCategory;
-import com.android.sdkuilib.repository.ISdkChangeListener;
-
-import org.eclipse.jface.dialogs.IInputValidator;
-import org.eclipse.jface.dialogs.InputDialog;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.viewers.CheckStateChangedEvent;
-import org.eclipse.jface.viewers.CheckboxTreeViewer;
-import org.eclipse.jface.viewers.ICheckStateListener;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.ITreeSelection;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeColumn;
-
-import java.util.ArrayList;
-
-
-public class RemotePackagesPage extends Composite implements ISdkChangeListener {
-
- private final UpdaterData mUpdaterData;
-
- private CheckboxTreeViewer mTreeViewerSources;
- private Tree mTreeSources;
- private TreeColumn mColumnSource;
- private Button mUpdateOnlyCheckBox;
- private Group mDescriptionContainer;
- private Button mAddSiteButton;
- private Button mDeleteSiteButton;
- private Button mRefreshButton;
- private Button mInstallSelectedButton;
- private Label mDescriptionLabel;
- private Label mSdkLocLabel;
-
-
-
- /**
- * Create the composite.
- * @param parent The parent of the composite.
- * @param updaterData An instance of {@link UpdaterData}.
- */
- RemotePackagesPage(Composite parent, UpdaterData updaterData) {
- super(parent, SWT.BORDER);
-
- mUpdaterData = updaterData;
-
- createContents(this);
- postCreate(); //$hide$
- }
-
- private void createContents(Composite parent) {
- parent.setLayout(new GridLayout(5, false));
-
- mSdkLocLabel = new Label(parent, SWT.NONE);
- mSdkLocLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 5, 1));
- mSdkLocLabel.setText("SDK Location: " +
- (mUpdaterData.getOsSdkRoot() != null ? mUpdaterData.getOsSdkRoot()
- : "<unknown>"));
-
- mTreeViewerSources = new CheckboxTreeViewer(parent, SWT.BORDER);
- mTreeViewerSources.addCheckStateListener(new ICheckStateListener() {
- public void checkStateChanged(CheckStateChangedEvent event) {
- onTreeCheckStateChanged(event); //$hide$
- }
- });
- mTreeSources = mTreeViewerSources.getTree();
- mTreeSources.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onTreeSelected(); //$hide$
- }
- });
- mTreeSources.setHeaderVisible(true);
- mTreeSources.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));
-
- mColumnSource = new TreeColumn(mTreeSources, SWT.NONE);
- mColumnSource.setWidth(289);
- mColumnSource.setText("Packages available for download");
-
- mDescriptionContainer = new Group(parent, SWT.NONE);
- mDescriptionContainer.setLayout(new GridLayout(1, false));
- mDescriptionContainer.setText("Description");
- mDescriptionContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 5, 1));
-
- mDescriptionLabel = new Label(mDescriptionContainer, SWT.NONE);
- mDescriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
- mDescriptionLabel.setText("Line1\nLine2\nLine3"); //$NON-NLS-1$
-
- mAddSiteButton = new Button(parent, SWT.NONE);
- mAddSiteButton.setText("Add Add-on Site...");
- mAddSiteButton.setToolTipText("Allows you to enter a new add-on site. " +
- "Such site can only contribute add-ons and extra packages.");
- mAddSiteButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onAddSiteSelected(); //$hide$
- }
- });
-
- mDeleteSiteButton = new Button(parent, SWT.NONE);
- mDeleteSiteButton.setText("Delete Add-on Site...");
- mDeleteSiteButton.setToolTipText("Allows you to remove an add-on site. " +
- "Built-in default sites cannot be removed.");
- mDeleteSiteButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onRemoveSiteSelected(); //$hide$
- }
- });
-
- mUpdateOnlyCheckBox = new Button(parent, SWT.CHECK);
- mUpdateOnlyCheckBox.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));
- mUpdateOnlyCheckBox.setText("Display updates only");
- mUpdateOnlyCheckBox.setToolTipText("When selected, only compatible non-obsolete update packages are shown in the list above.");
- mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());
- mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent arg0) {
- onShowUpdateOnly(); //$hide$
- }
- });
-
- mRefreshButton = new Button(parent, SWT.NONE);
- mRefreshButton.setText("Refresh");
- mRefreshButton.setToolTipText("Refreshes the list of packages from open sites.");
- mRefreshButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onRefreshSelected(); //$hide$
- }
- });
-
- mInstallSelectedButton = new Button(parent, SWT.NONE);
- mInstallSelectedButton.setText("Install Selected");
- mInstallSelectedButton.setToolTipText("Allows you to review all selected packages " +
- "and install them.");
- mInstallSelectedButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onInstallSelectedArchives(); //$hide$
- }
- });
- }
-
- @Override
- public void dispose() {
- mUpdaterData.removeListener(this);
- super.dispose();
- }
-
- @Override
- protected void checkSubclass() {
- // Disable the check that prevents subclassing of SWT components
- }
-
- // -- Start of internal part ----------
- // Hide everything down-below from SWT designer
- //$hide>>$
-
- /**
- * Called by the constructor right after {@link #createContents(Composite)}.
- */
- private void postCreate() {
- mUpdaterData.addListeners(this);
- adjustColumnsWidth();
- updateButtonsState();
- }
-
- /**
- * Adds a listener to adjust the columns width when the parent is resized.
- * <p/>
- * If we need something more fancy, we might want to use this:
- * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
- */
- private void adjustColumnsWidth() {
- // Add a listener to resize the column to the full width of the table
- ControlAdapter resizer = new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Rectangle r = mTreeSources.getClientArea();
- mColumnSource.setWidth(r.width);
- }
- };
-
- mTreeSources.addControlListener(resizer);
- resizer.controlResized(null);
- }
-
- /**
- * Called when an item in the package table viewer is selected.
- * If the items is an {@link IDescription} (as it should), this will display its long
- * description in the description area. Otherwise when the item is not of the expected
- * type or there is no selection, it empties the description area.
- */
- private void onTreeSelected() {
- updateButtonsState();
-
- ISelection sel = mTreeViewerSources.getSelection();
- if (sel instanceof ITreeSelection) {
- Object elem = ((ITreeSelection) sel).getFirstElement();
- if (elem instanceof IDescription) {
- mDescriptionLabel.setText(((IDescription) elem).getLongDescription());
- mDescriptionContainer.layout(true);
- return;
- }
- }
- mDescriptionLabel.setText(""); //$NON-NLS1-$
- }
-
- /**
- * Handle checking and unchecking of the tree items.
- *
- * When unchecking, all sub-tree items checkboxes are cleared too.
- * When checking a source, all of its packages are checked too.
- * When checking a package, only its compatible archives are checked.
- */
- private void onTreeCheckStateChanged(CheckStateChangedEvent event) {
- updateButtonsState();
-
- boolean b = event.getChecked();
- Object elem = event.getElement(); // Will be Archive or Package or RepoSource
-
- assert event.getSource() == mTreeViewerSources;
-
- // when deselecting, we just deselect all children too
- if (b == false) {
- mTreeViewerSources.setSubtreeChecked(elem, b);
- return;
- }
-
- ITreeContentProvider provider =
- (ITreeContentProvider) mTreeViewerSources.getContentProvider();
-
- // When selecting, we want to only select compatible archives
- // and expand the super nodes.
- expandItem(elem, provider);
- }
-
- private void expandItem(Object elem, ITreeContentProvider provider) {
- if (elem instanceof SdkSource || elem instanceof SdkSourceCategory) {
- mTreeViewerSources.setExpandedState(elem, true);
- for (Object pkg : provider.getChildren(elem)) {
- mTreeViewerSources.setChecked(pkg, true);
- expandItem(pkg, provider);
- }
- } else if (elem instanceof Package) {
- selectCompatibleArchives(elem, provider);
- }
- }
-
- private void selectCompatibleArchives(Object pkg, ITreeContentProvider provider) {
- for (Object archive : provider.getChildren(pkg)) {
- if (archive instanceof Archive) {
- mTreeViewerSources.setChecked(archive, ((Archive) archive).isCompatible());
- }
- }
- }
-
- private void onShowUpdateOnly() {
- SettingsController controller = mUpdaterData.getSettingsController();
- controller.setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection());
- controller.saveSettings();
-
- // Get the list of selected archives
- ArrayList<Archive> archives = new ArrayList<Archive>();
- for (Object element : mTreeViewerSources.getCheckedElements()) {
- if (element instanceof Archive) {
- archives.add((Archive) element);
- }
- // Deselect them all
- mTreeViewerSources.setChecked(element, false);
- }
-
- mTreeViewerSources.refresh();
-
- // Now reselect those that still exist in the tree but only if they
- // are compatible archives
- for (Archive a : archives) {
- if (a.isCompatible() && mTreeViewerSources.setChecked(a, true)) {
- // If we managed to select the archive, also select the parent package.
- // Technically we should only select the parent package if *all* the
- // compatible archives children are selected. In practice we'll rarely
- // have more than one compatible archive per package.
- mTreeViewerSources.setChecked(a.getParentPackage(), true);
- }
- }
-
- updateButtonsState();
- }
-
- private void onInstallSelectedArchives() {
- ArrayList<Archive> archives = new ArrayList<Archive>();
- for (Object element : mTreeViewerSources.getCheckedElements()) {
- if (element instanceof Archive) {
- archives.add((Archive) element);
- }
- }
-
- if (mUpdaterData != null) {
- mUpdaterData.updateOrInstallAll_WithGUI(
- archives,
- mUpdateOnlyCheckBox.getSelection() /* includeObsoletes */);
- }
- }
-
- private void onAddSiteSelected() {
-
- final SdkSource[] knowSources = mUpdaterData.getSources().getAllSources();
- String title = "Add Add-on Site URL";
-
- String msg =
- "This dialog lets you add the URL of a new add-on site.\n" +
- "\n" +
- "An add-on site can only provide new add-ons or \"user\" packages.\n" +
- "Add-on sites cannot provide standard Android platforms, docs or samples packages.\n" +
- "Inserting a URL here will not allow you to clone an official Android repository.\n" +
- "\n" +
- "Please enter the URL of the repository.xml for the new add-on site:";
-
- InputDialog dlg = new InputDialog(getShell(), title, msg, null, new IInputValidator() {
- public String isValid(String newText) {
-
- newText = newText == null ? null : newText.trim();
-
- if (newText == null || newText.length() == 0) {
- return "Error: URL field is empty. Please enter a URL.";
- }
-
- // A URL should have one of the following prefixes
- if (!newText.startsWith("file://") && //$NON-NLS-1$
- !newText.startsWith("ftp://") && //$NON-NLS-1$
- !newText.startsWith("http://") && //$NON-NLS-1$
- !newText.startsWith("https://")) { //$NON-NLS-1$
- return "Error: The URL must start by one of file://, ftp://, http:// or https://";
- }
-
- // Reject URLs that are already in the source list.
- // URLs are generally case-insensitive (except for file:// where it all depends
- // on the current OS so we'll ignore this case.)
- for (SdkSource s : knowSources) {
- if (newText.equalsIgnoreCase(s.getUrl())) {
- return "Error : This site is already listed.";
- }
- }
-
- return null;
- }
- });
-
- if (dlg.open() == Window.OK) {
- String url = dlg.getValue().trim();
- mUpdaterData.getSources().add(
- SdkSourceCategory.USER_ADDONS,
- new SdkAddonSource(url, null/*uiName*/));
- onRefreshSelected();
- }
- }
-
- private void onRemoveSiteSelected() {
- boolean changed = false;
-
- ISelection sel = mTreeViewerSources.getSelection();
- if (mUpdaterData != null && sel instanceof ITreeSelection) {
- for (Object c : ((ITreeSelection) sel).toList()) {
- if (c instanceof SdkSource) {
- SdkSource source = (SdkSource) c;
-
- if (mUpdaterData.getSources().hasSourceUrl(
- SdkSourceCategory.USER_ADDONS, source)) {
- String title = "Delete Add-on Site?";
-
- String msg = String.format("Are you sure you want to delete the add-on site '%1$s'?",
- source.getUrl());
-
- if (MessageDialog.openQuestion(getShell(), title, msg)) {
- mUpdaterData.getSources().remove(source);
- changed = true;
- }
- }
- }
- }
- }
-
- if (changed) {
- onRefreshSelected();
- }
- }
-
- private void onRefreshSelected() {
- if (mUpdaterData != null) {
- mUpdaterData.refreshSources(false /*forceFetching*/);
- }
- mTreeViewerSources.refresh();
- updateButtonsState();
- }
-
- private void updateButtonsState() {
- // We install archives, so there should be at least one checked archive.
- // Having sites or packages checked does not count.
- boolean hasCheckedArchive = false;
- Object[] checked = mTreeViewerSources.getCheckedElements();
- if (checked != null) {
- for (Object c : checked) {
- if (c instanceof Archive) {
- hasCheckedArchive = true;
- break;
- }
- }
- }
-
- // Is there a selected site Source?
- boolean hasSelectedUserSource = false;
- ISelection sel = mTreeViewerSources.getSelection();
- if (sel instanceof ITreeSelection) {
- for (Object c : ((ITreeSelection) sel).toList()) {
- if (c instanceof SdkSource &&
- mUpdaterData.getSources().hasSourceUrl(
- SdkSourceCategory.USER_ADDONS, (SdkSource) c)) {
- hasSelectedUserSource = true;
- break;
- }
- }
- }
-
- mAddSiteButton.setEnabled(true);
- mDeleteSiteButton.setEnabled(hasSelectedUserSource);
- mRefreshButton.setEnabled(true);
- mInstallSelectedButton.setEnabled(hasCheckedArchive);
-
- // set value on the show only update checkbox
- mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());
- }
-
- // --- Implementation of ISdkChangeListener ---
-
- public void onSdkLoaded() {
- onSdkReload();
- }
-
- public void onSdkReload() {
- RepoSourcesAdapter sources = mUpdaterData.getSourcesAdapter();
- mTreeViewerSources.setContentProvider(sources.getContentProvider());
- mTreeViewerSources.setLabelProvider( sources.getLabelProvider());
- mTreeViewerSources.setInput(sources);
- onTreeSelected();
- }
-
- public void preInstallHook() {
- // nothing to be done for now.
- }
-
- public void postInstallHook() {
- // nothing to be done for now.
- }
-
- // End of hiding from SWT Designer
- //$hide<<$
-}
+/*
+ * 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.sdkuilib.internal.repository;
+
+
+import com.android.sdklib.internal.repository.Archive;
+import com.android.sdklib.internal.repository.IDescription;
+import com.android.sdklib.internal.repository.Package;
+import com.android.sdklib.internal.repository.SdkAddonSource;
+import com.android.sdklib.internal.repository.SdkSource;
+import com.android.sdklib.internal.repository.SdkSourceCategory;
+import com.android.sdkuilib.repository.ISdkChangeListener;
+
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+
+import java.util.ArrayList;
+
+/**
+ * Page that displays remote repository & add-ons sources and let the user
+ * select packages for installation.
+ */
+public class RemotePackagesPage extends Composite implements ISdkChangeListener {
+
+ private final UpdaterData mUpdaterData;
+
+ private CheckboxTreeViewer mTreeViewerSources;
+ private Tree mTreeSources;
+ private TreeColumn mColumnSource;
+ private Button mUpdateOnlyCheckBox;
+ private Group mDescriptionContainer;
+ private Button mAddSiteButton;
+ private Button mDeleteSiteButton;
+ private Button mRefreshButton;
+ private Button mInstallSelectedButton;
+ private Label mDescriptionLabel;
+ private Label mSdkLocLabel;
+
+
+
+ /**
+ * Create the composite.
+ * @param parent The parent of the composite.
+ * @param updaterData An instance of {@link UpdaterData}.
+ */
+ RemotePackagesPage(Composite parent, UpdaterData updaterData) {
+ super(parent, SWT.BORDER);
+
+ mUpdaterData = updaterData;
+
+ createContents(this);
+ postCreate(); //$hide$
+ }
+
+ private void createContents(Composite parent) {
+ parent.setLayout(new GridLayout(5, false));
+
+ mSdkLocLabel = new Label(parent, SWT.NONE);
+ mSdkLocLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 5, 1));
+ mSdkLocLabel.setText("SDK Location: " +
+ (mUpdaterData.getOsSdkRoot() != null ? mUpdaterData.getOsSdkRoot()
+ : "<unknown>"));
+
+ mTreeViewerSources = new CheckboxTreeViewer(parent, SWT.BORDER);
+ mTreeViewerSources.addCheckStateListener(new ICheckStateListener() {
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ onTreeCheckStateChanged(event); //$hide$
+ }
+ });
+ mTreeSources = mTreeViewerSources.getTree();
+ mTreeSources.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onTreeSelected(); //$hide$
+ }
+ });
+ mTreeSources.setHeaderVisible(true);
+ mTreeSources.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));
+
+ mColumnSource = new TreeColumn(mTreeSources, SWT.NONE);
+ mColumnSource.setWidth(289);
+ mColumnSource.setText("Packages available for download");
+
+ mDescriptionContainer = new Group(parent, SWT.NONE);
+ mDescriptionContainer.setLayout(new GridLayout(1, false));
+ mDescriptionContainer.setText("Description");
+ mDescriptionContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 5, 1));
+
+ mDescriptionLabel = new Label(mDescriptionContainer, SWT.NONE);
+ mDescriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+ mDescriptionLabel.setText("Line1\nLine2\nLine3"); //$NON-NLS-1$
+
+ mAddSiteButton = new Button(parent, SWT.NONE);
+ mAddSiteButton.setText("Add Add-on Site...");
+ mAddSiteButton.setToolTipText("Allows you to enter a new add-on site. " +
+ "Such site can only contribute add-ons and extra packages.");
+ mAddSiteButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onAddSiteSelected(); //$hide$
+ }
+ });
+
+ mDeleteSiteButton = new Button(parent, SWT.NONE);
+ mDeleteSiteButton.setText("Delete Add-on Site...");
+ mDeleteSiteButton.setToolTipText("Allows you to remove an add-on site. " +
+ "Built-in default sites cannot be removed.");
+ mDeleteSiteButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onRemoveSiteSelected(); //$hide$
+ }
+ });
+
+ mUpdateOnlyCheckBox = new Button(parent, SWT.CHECK);
+ mUpdateOnlyCheckBox.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));
+ mUpdateOnlyCheckBox.setText("Display updates only");
+ mUpdateOnlyCheckBox.setToolTipText("When selected, only compatible non-obsolete update packages are shown in the list above.");
+ mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());
+ mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent arg0) {
+ onShowUpdateOnly(); //$hide$
+ }
+ });
+
+ mRefreshButton = new Button(parent, SWT.NONE);
+ mRefreshButton.setText("Refresh");
+ mRefreshButton.setToolTipText("Refreshes the list of packages from open sites.");
+ mRefreshButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onRefreshSelected(); //$hide$
+ }
+ });
+
+ mInstallSelectedButton = new Button(parent, SWT.NONE);
+ mInstallSelectedButton.setText("Install Selected");
+ mInstallSelectedButton.setToolTipText("Allows you to review all selected packages " +
+ "and install them.");
+ mInstallSelectedButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onInstallSelectedArchives(); //$hide$
+ }
+ });
+ }
+
+ @Override
+ public void dispose() {
+ mUpdaterData.removeListener(this);
+ super.dispose();
+ }
+
+ @Override
+ protected void checkSubclass() {
+ // Disable the check that prevents subclassing of SWT components
+ }
+
+ // -- Start of internal part ----------
+ // Hide everything down-below from SWT designer
+ //$hide>>$
+
+ /**
+ * Called by the constructor right after {@link #createContents(Composite)}.
+ */
+ private void postCreate() {
+ mUpdaterData.addListeners(this);
+ adjustColumnsWidth();
+ updateButtonsState();
+ }
+
+ /**
+ * Adds a listener to adjust the columns width when the parent is resized.
+ * <p/>
+ * If we need something more fancy, we might want to use this:
+ * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
+ */
+ private void adjustColumnsWidth() {
+ // Add a listener to resize the column to the full width of the table
+ ControlAdapter resizer = new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Rectangle r = mTreeSources.getClientArea();
+ mColumnSource.setWidth(r.width);
+ }
+ };
+
+ mTreeSources.addControlListener(resizer);
+ resizer.controlResized(null);
+ }
+
+ /**
+ * Called when an item in the package table viewer is selected.
+ * If the items is an {@link IDescription} (as it should), this will display its long
+ * description in the description area. Otherwise when the item is not of the expected
+ * type or there is no selection, it empties the description area.
+ */
+ private void onTreeSelected() {
+ updateButtonsState();
+
+ ISelection sel = mTreeViewerSources.getSelection();
+ if (sel instanceof ITreeSelection) {
+ Object elem = ((ITreeSelection) sel).getFirstElement();
+ if (elem instanceof IDescription) {
+ mDescriptionLabel.setText(((IDescription) elem).getLongDescription());
+ mDescriptionContainer.layout(true);
+ return;
+ }
+ }
+ mDescriptionLabel.setText(""); //$NON-NLS1-$
+ }
+
+ /**
+ * Handle checking and unchecking of the tree items.
+ *
+ * When unchecking, all sub-tree items checkboxes are cleared too.
+ * When checking a source, all of its packages are checked too.
+ * When checking a package, only its compatible archives are checked.
+ */
+ private void onTreeCheckStateChanged(CheckStateChangedEvent event) {
+ updateButtonsState();
+
+ boolean b = event.getChecked();
+ Object elem = event.getElement(); // Will be Archive or Package or RepoSource
+
+ assert event.getSource() == mTreeViewerSources;
+
+ // when deselecting, we just deselect all children too
+ if (b == false) {
+ mTreeViewerSources.setSubtreeChecked(elem, b);
+ return;
+ }
+
+ ITreeContentProvider provider =
+ (ITreeContentProvider) mTreeViewerSources.getContentProvider();
+
+ // When selecting, we want to only select compatible archives
+ // and expand the super nodes.
+ expandItem(elem, provider);
+ }
+
+ private void expandItem(Object elem, ITreeContentProvider provider) {
+ if (elem instanceof SdkSource || elem instanceof SdkSourceCategory) {
+ mTreeViewerSources.setExpandedState(elem, true);
+ for (Object pkg : provider.getChildren(elem)) {
+ mTreeViewerSources.setChecked(pkg, true);
+ expandItem(pkg, provider);
+ }
+ } else if (elem instanceof Package) {
+ selectCompatibleArchives(elem, provider);
+ }
+ }
+
+ private void selectCompatibleArchives(Object pkg, ITreeContentProvider provider) {
+ for (Object archive : provider.getChildren(pkg)) {
+ if (archive instanceof Archive) {
+ mTreeViewerSources.setChecked(archive, ((Archive) archive).isCompatible());
+ }
+ }
+ }
+
+ private void onShowUpdateOnly() {
+ SettingsController controller = mUpdaterData.getSettingsController();
+ controller.setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection());
+ controller.saveSettings();
+
+ // Get the list of selected archives
+ ArrayList<Archive> archives = new ArrayList<Archive>();
+ for (Object element : mTreeViewerSources.getCheckedElements()) {
+ if (element instanceof Archive) {
+ archives.add((Archive) element);
+ }
+ // Deselect them all
+ mTreeViewerSources.setChecked(element, false);
+ }
+
+ mTreeViewerSources.refresh();
+
+ // Now reselect those that still exist in the tree but only if they
+ // are compatible archives
+ for (Archive a : archives) {
+ if (a.isCompatible() && mTreeViewerSources.setChecked(a, true)) {
+ // If we managed to select the archive, also select the parent package.
+ // Technically we should only select the parent package if *all* the
+ // compatible archives children are selected. In practice we'll rarely
+ // have more than one compatible archive per package.
+ mTreeViewerSources.setChecked(a.getParentPackage(), true);
+ }
+ }
+
+ updateButtonsState();
+ }
+
+ private void onInstallSelectedArchives() {
+ ArrayList<Archive> archives = new ArrayList<Archive>();
+ for (Object element : mTreeViewerSources.getCheckedElements()) {
+ if (element instanceof Archive) {
+ archives.add((Archive) element);
+ }
+ }
+
+ if (mUpdaterData != null) {
+ mUpdaterData.updateOrInstallAll_WithGUI(
+ archives,
+ mUpdateOnlyCheckBox.getSelection() /* includeObsoletes */);
+ }
+ }
+
+ private void onAddSiteSelected() {
+ final SdkSource[] knowSources = mUpdaterData.getSources().getAllSources();
+ String title = "Add Add-on Site URL";
+
+ String msg =
+ "This dialog lets you add the URL of a new add-on site.\n" +
+ "\n" +
+ "An add-on site can only provide new add-ons or \"user\" packages.\n" +
+ "Add-on sites cannot provide standard Android platforms, docs or samples packages.\n" +
+ "Inserting a URL here will not allow you to clone an official Android repository.\n" +
+ "\n" +
+ "Please enter the URL of the repository.xml for the new add-on site:";
+
+ InputDialog dlg = new InputDialog(getShell(), title, msg, null, new IInputValidator() {
+ public String isValid(String newText) {
+
+ newText = newText == null ? null : newText.trim();
+
+ if (newText == null || newText.length() == 0) {
+ return "Error: URL field is empty. Please enter a URL.";
+ }
+
+ // A URL should have one of the following prefixes
+ if (!newText.startsWith("file://") && //$NON-NLS-1$
+ !newText.startsWith("ftp://") && //$NON-NLS-1$
+ !newText.startsWith("http://") && //$NON-NLS-1$
+ !newText.startsWith("https://")) { //$NON-NLS-1$
+ return "Error: The URL must start by one of file://, ftp://, http:// or https://";
+ }
+
+ // Reject URLs that are already in the source list.
+ // URLs are generally case-insensitive (except for file:// where it all depends
+ // on the current OS so we'll ignore this case.)
+ for (SdkSource s : knowSources) {
+ if (newText.equalsIgnoreCase(s.getUrl())) {
+ return "Error : This site is already listed.";
+ }
+ }
+
+ return null;
+ }
+ });
+
+ if (dlg.open() == Window.OK) {
+ String url = dlg.getValue().trim();
+ mUpdaterData.getSources().add(
+ SdkSourceCategory.USER_ADDONS,
+ new SdkAddonSource(url, null/*uiName*/));
+ onRefreshSelected();
+ }
+ }
+
+ private void onRemoveSiteSelected() {
+ boolean changed = false;
+
+ ISelection sel = mTreeViewerSources.getSelection();
+ if (mUpdaterData != null && sel instanceof ITreeSelection) {
+ for (Object c : ((ITreeSelection) sel).toList()) {
+ if (c instanceof SdkSource) {
+ SdkSource source = (SdkSource) c;
+
+ if (mUpdaterData.getSources().hasSourceUrl(
+ SdkSourceCategory.USER_ADDONS, source)) {
+ String title = "Delete Add-on Site?";
+
+ String msg = String.format("Are you sure you want to delete the add-on site '%1$s'?",
+ source.getUrl());
+
+ if (MessageDialog.openQuestion(getShell(), title, msg)) {
+ mUpdaterData.getSources().remove(source);
+ changed = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (changed) {
+ onRefreshSelected();
+ }
+ }
+
+ private void onRefreshSelected() {
+ if (mUpdaterData != null) {
+ mUpdaterData.refreshSources(false /*forceFetching*/);
+ }
+ mTreeViewerSources.refresh();
+ updateButtonsState();
+ }
+
+ private void updateButtonsState() {
+ // We install archives, so there should be at least one checked archive.
+ // Having sites or packages checked does not count.
+ boolean hasCheckedArchive = false;
+ Object[] checked = mTreeViewerSources.getCheckedElements();
+ if (checked != null) {
+ for (Object c : checked) {
+ if (c instanceof Archive) {
+ hasCheckedArchive = true;
+ break;
+ }
+ }
+ }
+
+ // Is there a selected site Source?
+ boolean hasSelectedUserSource = false;
+ ISelection sel = mTreeViewerSources.getSelection();
+ if (sel instanceof ITreeSelection) {
+ for (Object c : ((ITreeSelection) sel).toList()) {
+ if (c instanceof SdkSource &&
+ mUpdaterData.getSources().hasSourceUrl(
+ SdkSourceCategory.USER_ADDONS, (SdkSource) c)) {
+ hasSelectedUserSource = true;
+ break;
+ }
+ }
+ }
+
+ mAddSiteButton.setEnabled(true);
+ mDeleteSiteButton.setEnabled(hasSelectedUserSource);
+ mRefreshButton.setEnabled(true);
+ mInstallSelectedButton.setEnabled(hasCheckedArchive);
+
+ // set value on the show only update checkbox
+ mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());
+ }
+
+ // --- Implementation of ISdkChangeListener ---
+
+ public void onSdkLoaded() {
+ onSdkReload();
+ }
+
+ public void onSdkReload() {
+ RepoSourcesAdapter sources = mUpdaterData.getSourcesAdapter();
+ mTreeViewerSources.setContentProvider(sources.getContentProvider());
+ mTreeViewerSources.setLabelProvider( sources.getLabelProvider());
+ mTreeViewerSources.setInput(sources);
+ onTreeSelected();
+ }
+
+ public void preInstallHook() {
+ // nothing to be done for now.
+ }
+
+ public void postInstallHook() {
+ // nothing to be done for now.
+ }
+
+ // End of hiding from SWT Designer
+ //$hide<<$
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
index 8722e02..54bc068 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
@@ -316,13 +316,13 @@ public class RepoSourcesAdapter {
// get the installed packages
Package[] installedPackages = mUpdaterData.getInstalledPackages();
+ // we'll populate this package list with either upgrades or new packages.
ArrayList<Package> filteredList = new ArrayList<Package>();
// for each remote packages, we look for an existing version.
// If no existing version -> add to the list
// if existing version but with older revision -> add it to the list
for (Package remotePkg : remotePackages) {
- boolean newPkg = true;
// Obsolete packages are not offered as updates.
if (remotePkg.isObsolete()) {
@@ -332,20 +332,27 @@ public class RepoSourcesAdapter {
// For all potential packages, we also make sure that there's an archive for
// the current platform, or we simply skip them.
if (remotePkg.hasCompatibleArchive()) {
- for (Package installedPkg : installedPackages) {
+ boolean keepPkg = true;
+
+ nextPkg: for (Package installedPkg : installedPackages) {
UpdateInfo info = installedPkg.canBeUpdatedBy(remotePkg);
- if (info == UpdateInfo.UPDATE) {
- filteredList.add(remotePkg);
- newPkg = false;
- break; // there shouldn't be 2 revisions of the same package
- } else if (info != UpdateInfo.INCOMPATIBLE) {
- newPkg = false;
- break; // there shouldn't be 2 revisions of the same package
+ switch(info) {
+ case UPDATE:
+ // The remote package is an update to an existing one.
+ // We're done looking.
+ keepPkg = true;
+ break nextPkg;
+ case NOT_UPDATE:
+ // The remote package is the same as one that is already installed.
+ keepPkg = false;
+ break;
+ case INCOMPATIBLE:
+ // We can't compare and decide on incompatible things.
+ break;
}
}
- // if we have not found the same package, then we add it (it's a new package)
- if (newPkg) {
+ if (keepPkg) {
filteredList.add(remotePkg);
}
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java
index 2279b2d..6d5b5d4 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java
@@ -28,11 +28,6 @@ import java.util.Properties;
/**
* Performs an update using only a non-interactive console output with no GUI.
- * <p/>
- * TODO: It may be useful in the future to let the filter specify packages names
- * rather than package types, typically to let the user upgrade to a new platform.
- * This can be achieved easily by simply allowing package names in the pkgFilter
- * argument.
*/
public class UpdateNoWindow {
@@ -111,6 +106,15 @@ public class UpdateNoWindow {
mUpdaterData.updateOrInstallAll_NoGUI(pkgFilter, includeObsoletes, dryMode);
}
+ /**
+ * Lists remote packages available for install using 'android update sdk --no-ui'.
+ *
+ * @param includeObsoletes True to also list and install obsolete packages.
+ */
+ public void listRemotePackages(boolean includeObsoletes) {
+ mUpdaterData.listRemotePackages_NoGUI(includeObsoletes);
+ }
+
// -----
/**
@@ -170,45 +174,52 @@ public class UpdateNoWindow {
/**
* Sets the description in the current task dialog.
*/
- public void setDescription(String descriptionFormat, Object...args) {
+ public void setDescription(String format, Object...args) {
String last = mLastDesc;
- String line = String.format(" " + descriptionFormat, args);
+ String line = String.format(" " + format, args); //$NON-NLS-1$
// If the description contains a %, it generally indicates a recurring
// progress so we want a \r at the end.
- if (line.indexOf('%') > -1) {
- if (mLastProgressBase != null && line.startsWith(mLastProgressBase)) {
- line = " " + line.substring(mLastProgressBase.length());
+ int pos = line.indexOf('%');
+ if (pos > -1) {
+ String base = line.trim();
+ if (mLastProgressBase != null && base.startsWith(mLastProgressBase)) {
+ line = " " + base.substring(mLastProgressBase.length()); //$NON-NLS-1$
}
- line += "\r";
+ line += '\r';
} else {
- mLastProgressBase = line;
- line += "\n";
+ mLastProgressBase = line.trim();
+ line += '\n';
}
// Skip line if it's the same as the last one.
- if (last != null && last.equals(line)) {
+ if (last != null && last.equals(line.trim())) {
return;
}
- mLastDesc = line;
+ mLastDesc = line.trim();
// If the last line terminated with a \r but the new one doesn't, we need to
// insert a \n to avoid erasing the previous line.
if (last != null &&
- last.endsWith("\r") &&
- !line.endsWith("\r")) {
- line = "\n" + line;
+ last.endsWith("\r") && //$NON-NLS-1$
+ !line.endsWith("\r")) { //$NON-NLS-1$
+ line = '\n' + line;
}
- mSdkLog.printf("%s", line);
+ mSdkLog.printf("%s", line); //$NON-NLS-1$
}
- /**
- * Sets the description in the current task dialog.
- */
- public void setResult(String resultFormat, Object...args) {
- setDescription(resultFormat, args);
+ public void log(String format, Object...args) {
+ setDescription(" " + format, args); //$NON-NLS-1$
+ }
+
+ public void logError(String format, Object...args) {
+ setDescription(format, args);
+ }
+
+ public void logVerbose(String format, Object...args) {
+ // The ConsoleTask does not display verbose log messages.
}
/**
@@ -327,12 +338,20 @@ public class UpdateNoWindow {
return mRoot.isCancelRequested();
}
- public void setDescription(String descriptionFormat, Object... args) {
- mRoot.setDescription(descriptionFormat, args);
+ public void setDescription(String format, Object... args) {
+ mRoot.setDescription(format, args);
+ }
+
+ public void log(String format, Object... args) {
+ mRoot.log(format, args);
+ }
+
+ public void logError(String format, Object... args) {
+ mRoot.logError(format, args);
}
- public void setResult(String resultFormat, Object... args) {
- mRoot.setResult(resultFormat, args);
+ public void logVerbose(String format, Object... args) {
+ mRoot.logVerbose(format, args);
}
public void setProgressMax(int max) {
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
index b0160d9..1a3b5cb 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
@@ -44,6 +44,7 @@ import com.android.sdklib.internal.repository.AddonsListFetcher.Site;
import com.android.sdklib.repository.SdkAddonConstants;
import com.android.sdklib.repository.SdkAddonsListConstants;
import com.android.sdklib.repository.SdkRepoConstants;
+import com.android.sdklib.util.SparseIntArray;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.repository.ISdkChangeListener;
@@ -437,14 +438,14 @@ class UpdaterData implements IUpdaterData {
// This archive depends on a missing archive.
// We shouldn't get here.
// Skip it.
- monitor.setResult("Skipping '%1$s'; it depends on a missing package.",
+ monitor.log("Skipping '%1$s'; it depends on a missing package.",
archive.getParentPackage().getShortDescription());
continue nextArchive;
} else if (!installedArchives.contains(na)) {
// This archive depends on another one that was not installed.
// We shouldn't get here.
// Skip it.
- monitor.setResult("Skipping '%1$s'; it depends on '%2$s' which was not installed.",
+ monitor.logError("Skipping '%1$s'; it depends on '%2$s' which was not installed.",
archive.getParentPackage().getShortDescription(),
adep.getShortDescription());
continue nextArchive;
@@ -499,7 +500,7 @@ class UpdaterData implements IUpdaterData {
baos.toString());
}
- monitor.setResult(msg);
+ monitor.log(msg);
mSdkLog.error(t, msg);
} finally {
@@ -514,10 +515,10 @@ class UpdaterData implements IUpdaterData {
// Update the USB vendor ids for adb
try {
mSdkManager.updateAdb();
- monitor.setResult("Updated ADB to support the USB devices declared in the SDK add-ons.");
+ monitor.log("Updated ADB to support the USB devices declared in the SDK add-ons.");
} catch (Exception e) {
mSdkLog.error(e, "Update ADB failed");
- monitor.setResult("failed to update adb to support the USB devices declared in the SDK add-ons.");
+ monitor.logError("failed to update adb to support the USB devices declared in the SDK add-ons.");
}
}
@@ -709,23 +710,17 @@ class UpdaterData implements IUpdaterData {
}
/**
- * Tries to update all the *existing* local packages.
- * This version is intended to run without a GUI and
- * only outputs to the current {@link ISdkLog}.
+ * Fetches all archives available on the known remote sources.
*
- * @param pkgFilter A list of {@link SdkRepoConstants#NODES} to limit the type of packages
- * we can update. A null or empty list means to update everything possible.
- * @param includeObsoletes True to also list and install obsolete packages.
- * @param dryMode True to check what would be updated/installed but do not actually
- * download or install anything.
+ * Used by {@link UpdaterData#listRemotePackages_NoGUI} and
+ * {@link UpdaterData#updateOrInstallAll_NoGUI}.
+ *
+ * @param includeObsoletes True to also list obsolete packages.
+ * @return A list of potential {@link ArchiveInfo} to install.
*/
- @SuppressWarnings("unchecked")
- public void updateOrInstallAll_NoGUI(
- Collection<String> pkgFilter,
- boolean includeObsoletes,
- boolean dryMode) {
-
+ private List<ArchiveInfo> getRemoteArchives_NoGUI(boolean includeObsoletes) {
refreshSources(true);
+ loadRemoteAddonsList();
UpdaterLogic ul = new UpdaterLogic(this);
List<ArchiveInfo> archives = ul.computeUpdates(
@@ -734,7 +729,6 @@ class UpdaterData implements IUpdaterData {
getLocalSdkParser().getPackages(),
includeObsoletes);
- loadRemoteAddonsList();
ul.addNewPlatforms(
archives,
getSources(),
@@ -742,66 +736,81 @@ class UpdaterData implements IUpdaterData {
includeObsoletes);
Collections.sort(archives);
+ return archives;
+ }
+
+ /**
+ * Lists remote packages available for install using
+ * {@link UpdaterData#updateOrInstallAll_NoGUI}.
+ *
+ * @param includeObsoletes True to also list obsolete packages.
+ */
+ public void listRemotePackages_NoGUI(boolean includeObsoletes) {
+
+ List<ArchiveInfo> archives = getRemoteArchives_NoGUI(includeObsoletes);
+
+ mSdkLog.printf("Packages available for installation or update: %1$d\n", archives.size());
+
+ int index = 1;
+ for (ArchiveInfo ai : archives) {
+ Archive a = ai.getNewArchive();
+ if (a != null) {
+ Package p = a.getParentPackage();
+ if (p != null) {
+ mSdkLog.printf("%1$ 4d- %2$s\n",
+ index,
+ p.getShortDescription());
+ index++;
+ }
+ }
+ }
+ }
+
+ /**
+ * Tries to update all the *existing* local packages.
+ * This version is intended to run without a GUI and
+ * only outputs to the current {@link ISdkLog}.
+ *
+ * @param pkgFilter A list of {@link SdkRepoConstants#NODES} to limit the type of packages
+ * we can update. A null or empty list means to update everything possible.
+ * @param includeObsoletes True to also list and install obsolete packages.
+ * @param dryMode True to check what would be updated/installed but do not actually
+ * download or install anything.
+ */
+ public void updateOrInstallAll_NoGUI(
+ Collection<String> pkgFilter,
+ boolean includeObsoletes,
+ boolean dryMode) {
+
+ List<ArchiveInfo> archives = getRemoteArchives_NoGUI(includeObsoletes);
// Filter the selected archives to only keep the ones matching the filter
if (pkgFilter != null && pkgFilter.size() > 0 && archives != null && archives.size() > 0) {
- // Map filter types to an SdkRepository Package type.
+ // Map filter types to an SdkRepository Package type,
+ // e.g. create a map "platform" => PlatformPackage.class
HashMap<String, Class<? extends Package>> pkgMap =
new HashMap<String, Class<? extends Package>>();
- // Automatically find the classes matching the node names
- ClassLoader classLoader = getClass().getClassLoader();
- String basePackage = Package.class.getPackage().getName();
- for (String node : SdkRepoConstants.NODES) {
- // Capitalize the name
- String name = node.substring(0, 1).toUpperCase() + node.substring(1);
-
- // We can have one dash at most in a name. If it's present, we'll try
- // with the dash or with the next letter capitalized.
- int dash = name.indexOf('-');
- if (dash > 0) {
- name = name.replaceFirst("-", "");
- }
+ mapFilterToPackageClass(pkgMap, SdkRepoConstants.NODES);
+ mapFilterToPackageClass(pkgMap, SdkAddonConstants.NODES);
- for (int alternatives = 0; alternatives < 2; alternatives++) {
+ // Now intersect this with the pkgFilter requested by the user, in order to
+ // only keep the classes that the user wants to install.
+ // We also create a set with the package indices requested by the user.
- String fqcn = basePackage + "." + name + "Package"; //$NON-NLS-1$ //$NON-NLS-2$
- try {
- Class<? extends Package> clazz =
- (Class<? extends Package>) classLoader.loadClass(fqcn);
- if (clazz != null) {
- pkgMap.put(node, clazz);
- continue;
- }
- } catch (Throwable ignore) {
- }
+ HashSet<Class<? extends Package>> userFilteredClasses =
+ new HashSet<Class<? extends Package>>();
+ SparseIntArray userFilteredIndices = new SparseIntArray();
- if (alternatives == 0 && dash > 0) {
- // Try an alternative where the next letter after the dash
- // is converted to an upper case.
- name = name.substring(0, dash) +
- name.substring(dash, dash + 1).toUpperCase() +
- name.substring(dash + 1);
- } else {
- break;
- }
- }
- }
+ for (String type : pkgFilter) {
+ if (type.replaceAll("[0-9]+", "").length() == 0) { //$NON-NLS-1$ //$NON-NLS-2$
+ // An all-digit number is a package index requested by the user.
+ int index = Integer.parseInt(type);
+ userFilteredIndices.put(index, index);
- if (SdkRepoConstants.NODES.length != pkgMap.size()) {
- // Sanity check in case we forget to update this node array.
- // We don't cancel the operation though.
- mSdkLog.printf(
- "*** Filter Mismatch! ***\n" +
- "*** The package filter list has changed. Please report this.");
- }
+ } else if (pkgMap.containsKey(type)) {
+ userFilteredClasses.add(pkgMap.get(type));
- // Now make a set of the types that are allowed by the filter.
- HashSet<Class<? extends Package>> allowedPkgSet =
- new HashSet<Class<? extends Package>>();
- for (String type : pkgFilter) {
- if (pkgMap.containsKey(type)) {
- allowedPkgSet.add(pkgMap.get(type));
} else {
// This should not happen unless there's a mismatch in the package map.
mSdkLog.error(null, "Ignoring unknown package filter '%1$s'", type);
@@ -811,15 +820,24 @@ class UpdaterData implements IUpdaterData {
// we don't need the map anymore
pkgMap = null;
- Iterator<ArchiveInfo> it = archives.iterator();
- while (it.hasNext()) {
+ // Now filter the remote archives list to keep:
+ // - any package which class matches userFilteredClasses
+ // - any package index which matches userFilteredIndices
+
+ int index = 1;
+ for (Iterator<ArchiveInfo> it = archives.iterator(); it.hasNext(); ) {
boolean keep = false;
ArchiveInfo ai = it.next();
Archive a = ai.getNewArchive();
if (a != null) {
Package p = a.getParentPackage();
- if (p != null && allowedPkgSet.contains(p.getClass())) {
- keep = true;
+ if (p != null) {
+ if (userFilteredClasses.contains(p.getClass()) ||
+ userFilteredIndices.get(index) > 0) {
+ keep = true;
+ }
+
+ index++;
}
}
@@ -856,6 +874,52 @@ class UpdaterData implements IUpdaterData {
}
}
+ @SuppressWarnings("unchecked")
+ private void mapFilterToPackageClass(
+ HashMap<String, Class<? extends Package>> inOutPkgMap,
+ String[] nodes) {
+
+ // Automatically find the classes matching the node names
+ ClassLoader classLoader = getClass().getClassLoader();
+ String basePackage = Package.class.getPackage().getName();
+
+ for (String node : nodes) {
+ // Capitalize the name
+ String name = node.substring(0, 1).toUpperCase() + node.substring(1);
+
+ // We can have one dash at most in a name. If it's present, we'll try
+ // with the dash or with the next letter capitalized.
+ int dash = name.indexOf('-');
+ if (dash > 0) {
+ name = name.replaceFirst("-", "");
+ }
+
+ for (int alternatives = 0; alternatives < 2; alternatives++) {
+
+ String fqcn = basePackage + '.' + name + "Package"; //$NON-NLS-1$
+ try {
+ Class<? extends Package> clazz =
+ (Class<? extends Package>) classLoader.loadClass(fqcn);
+ if (clazz != null) {
+ inOutPkgMap.put(node, clazz);
+ continue;
+ }
+ } catch (Throwable ignore) {
+ }
+
+ if (alternatives == 0 && dash > 0) {
+ // Try an alternative where the next letter after the dash
+ // is converted to an upper case.
+ name = name.substring(0, dash) +
+ name.substring(dash, dash + 1).toUpperCase() +
+ name.substring(dash + 1);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
/**
* Refresh all sources. This is invoked either internally (reusing an existing monitor)
* or as a UI callback on the remote page "Refresh" button (in which case the monitor is
@@ -877,6 +941,7 @@ class UpdaterData implements IUpdaterData {
}
SdkSource[] sources = mSources.getAllSources();
+ monitor.setDescription("Refresh Sources");
monitor.setProgressMax(monitor.getProgress() + sources.length);
for (SdkSource source : sources) {
if (forceFetching ||
@@ -919,19 +984,19 @@ class UpdaterData implements IUpdaterData {
//
// Since SDK_TEST_URLS can contain many such URLs, we take the first one that
// matches our criteria.
- String url = System.getenv("SDK_TEST_URLS");
+ String url = System.getenv("SDK_TEST_URLS"); //$NON-NLS-1$
if (url == null) {
// No override, use the canonical URL.
url = SdkAddonsListConstants.URL_ADDON_LIST;
} else {
- String[] urls = url.split(";");
+ String[] urls = url.split(";"); //$NON-NLS-1$
url = null;
for (String u : urls) {
u = u.trim();
// This is an URL that comes from the env var. We expect it to either
// end with a / or the canonical name, otherwise we don't use it.
- if (u.endsWith("/")) {
+ if (u.endsWith("/")) { //$NON-NLS-1$
url = u + SdkAddonsListConstants.URL_DEFAULT_FILENAME;
break;
} else if (u.endsWith(SdkAddonsListConstants.URL_DEFAULT_FILENAME)) {
@@ -943,7 +1008,7 @@ class UpdaterData implements IUpdaterData {
if (url != null) {
if (getSettingsController().getForceHttp()) {
- url = url.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$
+ url = url.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$
}
AddonsListFetcher fetcher = new AddonsListFetcher();
@@ -959,6 +1024,8 @@ class UpdaterData implements IUpdaterData {
mStateFetchRemoteAddonsList = 1;
}
}
+
+ monitor.setDescription("Fetched Add-ons List successfully");
}
/**
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
index 91afed6..c2472d8 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
@@ -40,9 +40,12 @@ import com.android.sdklib.internal.repository.ToolPackage;
import com.android.sdklib.internal.repository.Package.UpdateInfo;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* The logic to compute which packages to install, based on the choices
@@ -120,6 +123,20 @@ class UpdaterLogic {
/**
* Finds new packages that the user does not have in his/her local SDK
* and adds them to the list of archives to install.
+ * <p/>
+ * The default is to only find "new" platforms, that is anything more
+ * recent than the highest platform currently installed.
+ * A side effect is that for an empty SDK install this will list *all*
+ * platforms available (since there's no "highest" installed platform.)
+ *
+ * @param archives The in-out list of archives to install. Typically the
+ * list is not empty at first as it should contain any archives that is
+ * already scheduled for install. This method will add to the list.
+ * @param sources The list of all sources, to fetch them as necessary.
+ * @param localPkgs The list of all currently installed packages.
+ * @param includeObsoletes When true, this will list all platform
+ * (included these lower than the highest installed one) as well as
+ * all obsolete packages of these platforms.
*/
public void addNewPlatforms(
Collection<ArchiveInfo> archives,
@@ -136,32 +153,34 @@ class UpdaterLogic {
float currentAddonScore = 0;
float currentDocScore = 0;
HashMap<String, Float> currentExtraScore = new HashMap<String, Float>();
- if (localPkgs != null) {
- for (Package p : localPkgs) {
- int rev = p.getRevision();
- int api = 0;
- boolean isPreview = false;
- if (p instanceof IPackageVersion) {
- AndroidVersion vers = ((IPackageVersion) p).getVersion();
- api = vers.getApiLevel();
- isPreview = vers.isPreview();
- }
-
- // The score is 10*api + (1 if preview) + rev/100
- // This allows previews to rank above a non-preview and
- // allows revisions to rank appropriately.
- float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;
+ if (!includeObsoletes) {
+ if (localPkgs != null) {
+ for (Package p : localPkgs) {
+ int rev = p.getRevision();
+ int api = 0;
+ boolean isPreview = false;
+ if (p instanceof IPackageVersion) {
+ AndroidVersion vers = ((IPackageVersion) p).getVersion();
+ api = vers.getApiLevel();
+ isPreview = vers.isPreview();
+ }
- if (p instanceof PlatformPackage) {
- currentPlatformScore = Math.max(currentPlatformScore, score);
- } else if (p instanceof SamplePackage) {
- currentSampleScore = Math.max(currentSampleScore, score);
- } else if (p instanceof AddonPackage) {
- currentAddonScore = Math.max(currentAddonScore, score);
- } else if (p instanceof ExtraPackage) {
- currentExtraScore.put(((ExtraPackage) p).getPath(), score);
- } else if (p instanceof DocPackage) {
- currentDocScore = Math.max(currentDocScore, score);
+ // The score is 10*api + (1 if preview) + rev/100
+ // This allows previews to rank above a non-preview and
+ // allows revisions to rank appropriately.
+ float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;
+
+ if (p instanceof PlatformPackage) {
+ currentPlatformScore = Math.max(currentPlatformScore, score);
+ } else if (p instanceof SamplePackage) {
+ currentSampleScore = Math.max(currentSampleScore, score);
+ } else if (p instanceof AddonPackage) {
+ currentAddonScore = Math.max(currentAddonScore, score);
+ } else if (p instanceof ExtraPackage) {
+ currentExtraScore.put(((ExtraPackage) p).getPath(), score);
+ } else if (p instanceof DocPackage) {
+ currentDocScore = Math.max(currentDocScore, score);
+ }
}
}
}
@@ -460,7 +479,7 @@ class UpdaterLogic {
// - platform: *might* depends on tools of rev >= min-tools-rev
// - extra: *might* depends on platform with api >= min-api-level
- ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>();
+ Set<ArchiveInfo> aiFound = new HashSet<ArchiveInfo>();
if (pkg instanceof IPlatformDependency) {
ArchiveInfo ai = findPlatformDependency(
@@ -472,7 +491,7 @@ class UpdaterLogic {
localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
@@ -487,7 +506,7 @@ class UpdaterLogic {
localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
@@ -502,7 +521,7 @@ class UpdaterLogic {
localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
@@ -517,7 +536,7 @@ class UpdaterLogic {
localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
@@ -532,12 +551,14 @@ class UpdaterLogic {
localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
- if (list.size() > 0) {
- return list.toArray(new ArchiveInfo[list.size()]);
+ if (aiFound.size() > 0) {
+ ArchiveInfo[] result = aiFound.toArray(new ArchiveInfo[aiFound.size()]);
+ Arrays.sort(result);
+ return result;
}
return null;
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
index 19d3916..7514dea 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
@@ -22,6 +22,7 @@ import com.android.sdklib.SdkConstants;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.internal.tasks.ProgressTaskFactory;
import com.android.sdkuilib.repository.ISdkChangeListener;
+import com.android.sdkuilib.repository.IUpdaterWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
@@ -31,7 +32,8 @@ import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.List;
@@ -41,9 +43,14 @@ import java.lang.reflect.Constructor;
import java.util.ArrayList;
/**
- * This is the private implementation of the UpdateWindow.
+ * This is the private implementation of the UpdateWindow for the
+ * first version of the SDK Manager.
+ * <p/>
+ * This window has a sash, with a list of available pages on the left
+ * (AVD list, settings, about, installed packages, available packages)
+ * and the corresponding page on the right.
*/
-public class UpdaterWindowImpl {
+public class UpdaterWindowImpl implements IUpdaterWindow {
private final Shell mParentShell;
/** Internal data shared between the window and its pages. */
@@ -65,14 +72,13 @@ public class UpdaterWindowImpl {
// --- UI members ---
- protected Shell mAndroidSdkUpdater;
- private SashForm mSashForm;
+ protected Shell mShell;
private List mPageList;
private Composite mPagesRootComposite;
- private LocalPackagesPage mLocalPackagePage;
- private RemotePackagesPage mRemotePackagesPage;
private AvdManagerPage mAvdManagerPage;
private StackLayout mStackLayout;
+ private LocalPackagesPage mLocalPackagePage;
+ private RemotePackagesPage mRemotePackagesPage;
/**
* Creates a new window. Caller must call open(), which will block.
@@ -87,7 +93,7 @@ public class UpdaterWindowImpl {
}
/**
- * Open the window.
+ * Opens the window.
* @wbp.parser.entryPoint
*/
public void open() {
@@ -95,13 +101,15 @@ public class UpdaterWindowImpl {
Display.setAppName("Android"); //$hide$ (hide from SWT designer)
}
+ createShell();
+ preCreateContent();
createContents();
- mAndroidSdkUpdater.open();
- mAndroidSdkUpdater.layout();
+ mShell.open();
+ mShell.layout();
- if (postCreate()) { //$hide$ (hide from SWT designer)
+ if (postCreateContent()) { //$hide$ (hide from SWT designer)
Display display = Display.getDefault();
- while (!mAndroidSdkUpdater.isDisposed()) {
+ while (!mShell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
@@ -111,27 +119,34 @@ public class UpdaterWindowImpl {
dispose(); //$hide$
}
- /**
- * Create contents of the window.
- */
- protected void createContents() {
- mAndroidSdkUpdater = new Shell(mParentShell, SWT.SHELL_TRIM);
- mAndroidSdkUpdater.addDisposeListener(new DisposeListener() {
+ private void createShell() {
+ mShell = new Shell(mParentShell, SWT.SHELL_TRIM);
+ mShell.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
onAndroidSdkUpdaterDispose(); //$hide$ (hide from SWT designer)
}
});
- FillLayout fl;
- mAndroidSdkUpdater.setLayout(fl = new FillLayout(SWT.HORIZONTAL));
- fl.marginHeight = fl.marginWidth = 5;
- mAndroidSdkUpdater.setMinimumSize(new Point(200, 50));
- mAndroidSdkUpdater.setSize(745, 433);
- mAndroidSdkUpdater.setText("Android SDK and AVD Manager");
+ GridLayout glShell = new GridLayout(2, false);
+ glShell.verticalSpacing = 0;
+ glShell.horizontalSpacing = 0;
+ glShell.marginWidth = 0;
+ glShell.marginHeight = 0;
+ mShell.setLayout(glShell);
+
+ mShell.setMinimumSize(new Point(500, 300));
+ mShell.setSize(700, 500);
+ mShell.setText("Android SDK and AVD Manager");
+ }
- mSashForm = new SashForm(mAndroidSdkUpdater, SWT.NONE);
+ /**
+ * Create contents of the window.
+ */
+ private void createContents() {
+ SashForm sashForm = new SashForm(mShell, SWT.NONE);
+ sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
- mPageList = new List(mSashForm, SWT.BORDER);
+ mPageList = new List(sashForm, SWT.BORDER);
mPageList.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
@@ -139,13 +154,15 @@ public class UpdaterWindowImpl {
}
});
- mPagesRootComposite = new Composite(mSashForm, SWT.NONE);
- mStackLayout = new StackLayout();
- mPagesRootComposite.setLayout(mStackLayout);
+ createPagesRoot(sashForm);
- createPages();
+ sashForm.setWeights(new int[] {150, 576});
+ }
- mSashForm.setWeights(new int[] {150, 576});
+ private void createPagesRoot(Composite parent) {
+ mPagesRootComposite = new Composite(parent, SWT.NONE);
+ mStackLayout = new StackLayout();
+ mPagesRootComposite.setLayout(mStackLayout);
}
// -- Start of internal part ----------
@@ -166,7 +183,7 @@ public class UpdaterWindowImpl {
* @param title The title of the page.
* @param pageClass The {@link Composite}-derived class that will implement the page.
*/
- public void registerExtraPage(String title, Class<? extends Composite> pageClass) {
+ public void registerPage(String title, Class<? extends Composite> pageClass) {
if (mExtraPages == null) {
mExtraPages = new ArrayList<Object[]>();
}
@@ -208,26 +225,22 @@ public class UpdaterWindowImpl {
// --- Internals & UI Callbacks -----------
-
/**
- * Called by {@link #createContents()} to generate the pages that can be
+ * Called by {@link #postCreateContent()} to generate the pages that can be
* displayed in the window.
- * <p/>
- * Implementation detail: This is extracted from {@link #createContents()}
- * so that we can skip it when using WindowsBuilder, since {@link #mUpdaterData}
- * will then be null.
*/
- private void createPages() {
+ protected void createPages() {
mAvdManagerPage = new AvdManagerPage(mPagesRootComposite, mUpdaterData);
+
mLocalPackagePage = new LocalPackagesPage(mPagesRootComposite, mUpdaterData);
mRemotePackagesPage = new RemotePackagesPage(mPagesRootComposite, mUpdaterData);
- }
- /**
- * Helper to return the SWT shell.
- */
- private Shell getShell() {
- return mAndroidSdkUpdater;
+ addPage(mAvdManagerPage, "Virtual devices");
+
+ addPage(mLocalPackagePage, "Installed packages");
+ addPage(mRemotePackagesPage, "Available packages");
+
+ addExtraPages();
}
/**
@@ -254,44 +267,34 @@ public class UpdaterWindowImpl {
if (mUpdaterData != null) {
ImageFactory imgFactory = mUpdaterData.getImageFactory();
if (imgFactory != null) {
- mAndroidSdkUpdater.setImage(imgFactory.getImageByName(imageName));
+ mShell.setImage(imgFactory.getImageByName(imageName));
}
}
}
/**
+ * Called before the UI is created.
+ */
+ private void preCreateContent() {
+ mUpdaterData.setWindowShell(mShell);
+ mTaskFactory = new ProgressTaskFactory(mShell);
+ mUpdaterData.setTaskFactory(mTaskFactory);
+ mUpdaterData.setImageFactory(new ImageFactory(mShell.getDisplay()));
+ }
+
+ /**
* Once the UI has been created, initializes the content.
* This creates the pages, selects the first one, setup sources and scan for local folders.
*
* Returns true if we should show the window.
*/
- private boolean postCreate() {
- mUpdaterData.setWindowShell(getShell());
- mTaskFactory = new ProgressTaskFactory(getShell());
- mUpdaterData.setTaskFactory(mTaskFactory);
- mUpdaterData.setImageFactory(new ImageFactory(getShell().getDisplay()));
-
- setWindowImage(mAndroidSdkUpdater);
-
- addPage(mAvdManagerPage, "Virtual devices");
- addPage(mLocalPackagePage, "Installed packages");
- addPage(mRemotePackagesPage, "Available packages");
- addExtraPages();
-
- int pageIndex = 0;
- int i = 0;
- for (Composite p : mPages) {
- if (p.getClass().equals(mInitialPage)) {
- pageIndex = i;
- break;
- }
- i++;
- }
- displayPage(pageIndex);
- mPageList.setSelection(pageIndex);
+ private boolean postCreateContent() {
+ setWindowImage(mShell);
+ createPages();
setupSources();
initializeSettings();
+ selectInitialPage();
if (mUpdaterData.checkIfInitFailed()) {
return false;
@@ -323,10 +326,12 @@ public class UpdaterWindowImpl {
* Each page is a {@link Composite}. The title of the page is stored in the
* {@link Composite#getData()} field.
*/
- private void addPage(Composite page, String title) {
+ protected void addPage(Composite page, String title) {
page.setData(title);
mPages.add(page);
- mPageList.add(title);
+ if (mPageList != null) {
+ mPageList.add(title);
+ }
}
/**
@@ -335,7 +340,7 @@ public class UpdaterWindowImpl {
* to the page list.
*/
@SuppressWarnings("unchecked")
- private void addExtraPages() {
+ protected void addExtraPages() {
if (mExtraPages == null) {
return;
}
@@ -371,7 +376,7 @@ public class UpdaterWindowImpl {
* If this is not an internal page change, displays the given page.
*/
private void onPageListSelected() {
- if (mInternalPageChange == false) {
+ if (mInternalPageChange == false && mPageList != null) {
int index = mPageList.getSelectionIndex();
if (index >= 0) {
displayPage(index);
@@ -390,11 +395,15 @@ public class UpdaterWindowImpl {
mStackLayout.topControl = page;
mPagesRootComposite.layout(true);
- if (!mInternalPageChange) {
+ if (!mInternalPageChange && mPageList != null) {
mInternalPageChange = true;
mPageList.setSelection(index);
mInternalPageChange = false;
}
+
+ if (page instanceof IPageListener) {
+ ((IPageListener) page).onPageSelected();
+ }
}
}
@@ -403,7 +412,6 @@ public class UpdaterWindowImpl {
*/
private void setupSources() {
mUpdaterData.setupDefaultSources();
- mRemotePackagesPage.onSdkReload();
}
/**
@@ -427,6 +435,28 @@ public class UpdaterWindowImpl {
}
}
+ /**
+ * Select and show the initial page.
+ * This will be either the page which class matches {@link #mInitialPage} or the
+ * first one in the list.
+ */
+ private void selectInitialPage() {
+ int pageIndex = 0;
+ int i = 0;
+ for (Composite p : mPages) {
+ if (p.getClass().equals(mInitialPage)) {
+ pageIndex = i;
+ break;
+ }
+ i++;
+ }
+
+ displayPage(pageIndex);
+ if (mPageList != null) {
+ mPageList.setSelection(pageIndex);
+ }
+ }
+
// End of hiding from SWT Designer
//$hide<<$
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java
new file mode 100755
index 0000000..6b3cd66
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2011 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.sdkuilib.internal.repository;
+
+
+import com.android.menubar.IMenuBarCallback;
+import com.android.menubar.MenuBarEnhancer;
+import com.android.sdklib.ISdkLog;
+import com.android.sdklib.SdkConstants;
+import com.android.sdkuilib.internal.repository.PackagesPage.MenuAction;
+import com.android.sdkuilib.internal.repository.icons.ImageFactory;
+import com.android.sdkuilib.internal.tasks.ProgressView;
+import com.android.sdkuilib.internal.tasks.ProgressViewFactory;
+import com.android.sdkuilib.repository.ISdkChangeListener;
+import com.android.sdkuilib.repository.IUpdaterWindow;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.ProgressBar;
+import org.eclipse.swt.widgets.Shell;
+
+import java.util.ArrayList;
+
+/**
+ * This is the private implementation of the UpdateWindow
+ * for the second version of the SDK Manager.
+ * <p/>
+ * This window features only one embedded page, the combined installed+available package list.
+ */
+public class UpdaterWindowImpl2 implements IUpdaterWindow {
+
+ private static final String APP_NAME = "Android SDK Manager";
+ private final Shell mParentShell;
+ /** Internal data shared between the window and its pages. */
+ private final UpdaterData mUpdaterData;
+ /** A list of extra pages to instantiate. Each entry is an object array with 2 elements:
+ * the string title and the Composite class to instantiate to create the page. */
+ private ArrayList<Object[]> mExtraPages;
+ /** Sets whether the auto-update wizard will be shown when opening the window. */
+ private boolean mRequestAutoUpdate;
+
+ // --- UI members ---
+
+ protected Shell mShell;
+ private PackagesPage mPkgPage;
+ private ProgressBar mProgressBar;
+ private Label mStatusText;
+ private ImgDisabledButton mButtonStop;
+ private ToggleButton mButtonDetails;
+
+ /**
+ * Creates a new window. Caller must call open(), which will block.
+ *
+ * @param parentShell Parent shell.
+ * @param sdkLog Logger. Cannot be null.
+ * @param osSdkRoot The OS path to the SDK root.
+ */
+ public UpdaterWindowImpl2(Shell parentShell, ISdkLog sdkLog, String osSdkRoot) {
+ mParentShell = parentShell;
+ mUpdaterData = new UpdaterData(osSdkRoot, sdkLog);
+ }
+
+ /**
+ * Opens the window.
+ * @wbp.parser.entryPoint
+ */
+ public void open() {
+ if (mParentShell == null) {
+ Display.setAppName(APP_NAME); //$hide$ (hide from SWT designer)
+ }
+
+ createShell();
+ preCreateContent();
+ createContents();
+ createMenuBar();
+ mShell.open();
+ mShell.layout();
+
+ if (postCreateContent()) { //$hide$ (hide from SWT designer)
+ Display display = Display.getDefault();
+ while (!mShell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ }
+
+ dispose(); //$hide$
+ }
+
+ private void createShell() {
+ mShell = new Shell(mParentShell, SWT.SHELL_TRIM);
+ mShell.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ onAndroidSdkUpdaterDispose(); //$hide$ (hide from SWT designer)
+ }
+ });
+
+ GridLayout glShell = new GridLayout(2, false);
+ glShell.verticalSpacing = 0;
+ glShell.horizontalSpacing = 0;
+ glShell.marginWidth = 0;
+ glShell.marginHeight = 0;
+ mShell.setLayout(glShell);
+
+ mShell.setMinimumSize(new Point(500, 300));
+ mShell.setSize(700, 500);
+ mShell.setText(APP_NAME);
+ }
+
+ private void createContents() {
+
+ mPkgPage = new PackagesPage(mShell, mUpdaterData);
+ mPkgPage.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+
+ Composite composite1 = new Composite(mShell, SWT.NONE);
+ composite1.setLayout(new GridLayout(1, false));
+ composite1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ mProgressBar = new ProgressBar(composite1, SWT.NONE);
+ mProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ mStatusText = new Label(composite1, SWT.NONE);
+ mStatusText.setText("Status Placeholder"); //$NON-NLS-1$ placeholder
+ mStatusText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ Composite composite2 = new Composite(mShell, SWT.NONE);
+ composite2.setLayout(new GridLayout(2, false));
+
+ mButtonStop = new ImgDisabledButton(composite2, SWT.NONE,
+ getImage("stop_enabled_16.png"), //$NON-NLS-1$
+ getImage("stop_disabled_16.png")); //$NON-NLS-1$
+ mButtonStop.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ onStopSelected();
+ }
+ });
+
+ mButtonDetails = new ToggleButton(composite2, SWT.NONE,
+ getImage("collapsed_16.png"), //$NON-NLS-1$
+ getImage("expanded_16.png")); //$NON-NLS-1$
+ mButtonDetails.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ onToggleDetails();
+ }
+ });
+ }
+
+ private void createMenuBar() {
+
+ Menu menuBar = new Menu(mShell, SWT.BAR);
+ mShell.setMenuBar(menuBar);
+
+ MenuItem menuBarPackages = new MenuItem(menuBar, SWT.CASCADE);
+ menuBarPackages.setText("Packages");
+
+ Menu menuPkgs = new Menu(menuBarPackages);
+ menuBarPackages.setMenu(menuPkgs);
+
+ MenuItem showUpdatesNew = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuStyle());
+ showUpdatesNew.setText(
+ MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG, showUpdatesNew);
+
+ MenuItem showInstalled = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuStyle());
+ showInstalled.setText(
+ MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_INSTALLED_PKG, showInstalled);
+
+ MenuItem showObsoletePackages = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuStyle());
+ showObsoletePackages.setText(
+ MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_OBSOLETE_PKG, showObsoletePackages);
+
+ MenuItem showArchives = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuStyle());
+ showArchives.setText(
+ MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_ARCHIVES, showArchives);
+
+ new MenuItem(menuPkgs, SWT.SEPARATOR);
+
+ MenuItem sortByApi = new MenuItem(menuPkgs,
+ MenuAction.SORT_API_LEVEL.getMenuStyle());
+ sortByApi.setText(
+ MenuAction.SORT_API_LEVEL.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.SORT_API_LEVEL, sortByApi);
+
+ MenuItem sortBySource = new MenuItem(menuPkgs,
+ MenuAction.SORT_SOURCE.getMenuStyle());
+ sortBySource.setText(
+ MenuAction.SORT_SOURCE.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.SORT_SOURCE, sortBySource);
+
+ new MenuItem(menuPkgs, SWT.SEPARATOR);
+
+ MenuItem reload = new MenuItem(menuPkgs,
+ MenuAction.RELOAD.getMenuStyle());
+ reload.setText(
+ MenuAction.RELOAD.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.RELOAD, reload);
+
+ MenuItem menuBarTools = new MenuItem(menuBar, SWT.CASCADE);
+ menuBarTools.setText("Tools");
+
+ Menu menuTools = new Menu(menuBarTools);
+ menuBarTools.setMenu(menuTools);
+
+ MenuItem manageAvds = new MenuItem(menuTools, SWT.NONE);
+ manageAvds.setText("Manage AVDs...");
+
+ MenuItem manageSources = new MenuItem(menuTools,
+ MenuAction.SHOW_ADDON_SITES.getMenuStyle());
+ manageSources.setText(
+ MenuAction.SHOW_ADDON_SITES.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.SHOW_ADDON_SITES, manageSources);
+
+ MenuBarEnhancer.setupMenu(APP_NAME, menuTools, new IMenuBarCallback() {
+ public void onPreferencesMenuSelected() {
+ // TODO: plug settings page here
+ MessageDialog.openInformation(mShell, "test", "on prefs");
+ }
+
+ public void onAboutMenuSelected() {
+ // TODO: plug about page here
+ MessageDialog.openInformation(mShell, "test", "on about");
+ }
+
+ public void printError(String format, Object... args) {
+ if (mUpdaterData != null) {
+ // TODO: right now dump to stderr. Use sdklog later.
+ //mUpdaterData.getSdkLog().error(null, format, args);
+ System.err.printf(format, args);
+ }
+ }
+ });
+ }
+
+ private Image getImage(String filename) {
+ if (mUpdaterData != null) {
+ ImageFactory imgFactory = mUpdaterData.getImageFactory();
+ if (imgFactory != null) {
+ return imgFactory.getImageByName(filename);
+ }
+ }
+ return null;
+ }
+
+
+ // -- Start of internal part ----------
+ // Hide everything down-below from SWT designer
+ //$hide>>$
+
+ // --- Public API -----------
+
+
+ /**
+ * Registers an extra page for the updater window.
+ * <p/>
+ * Pages must derive from {@link Composite} and implement a constructor that takes
+ * a single parent {@link Composite} argument.
+ * <p/>
+ * All pages must be registered before the call to {@link #open()}.
+ *
+ * @param title The title of the page.
+ * @param pageClass The {@link Composite}-derived class that will implement the page.
+ */
+ public void registerPage(String title, Class<? extends Composite> pageClass) {
+ if (mExtraPages == null) {
+ mExtraPages = new ArrayList<Object[]>();
+ }
+ mExtraPages.add(new Object[]{ title, pageClass });
+ }
+
+ /**
+ * Indicate the initial page that should be selected when the window opens.
+ * This must be called before the call to {@link #open()}.
+ * If null or if the page class is not found, the first page will be selected.
+ */
+ public void setInitialPage(Class<? extends Composite> pageClass) {
+ // Unused in this case. This window display only one page.
+ }
+
+ /**
+ * Sets whether the auto-update wizard will be shown when opening the window.
+ * <p/>
+ * This must be called before the call to {@link #open()}.
+ */
+ public void setRequestAutoUpdate(boolean requestAutoUpdate) {
+ mRequestAutoUpdate = requestAutoUpdate;
+ }
+
+ /**
+ * Adds a new listener to be notified when a change is made to the content of the SDK.
+ */
+ public void addListener(ISdkChangeListener listener) {
+ mUpdaterData.addListeners(listener);
+ }
+
+ /**
+ * Removes a new listener to be notified anymore when a change is made to the content of
+ * the SDK.
+ */
+ public void removeListener(ISdkChangeListener listener) {
+ mUpdaterData.removeListener(listener);
+ }
+
+ // --- Internals & UI Callbacks -----------
+
+ /**
+ * Called before the UI is created.
+ */
+ private void preCreateContent() {
+ mUpdaterData.setWindowShell(mShell);
+ // We need the UI factory to create the UI
+ mUpdaterData.setImageFactory(new ImageFactory(mShell.getDisplay()));
+ // Note: we can't create the TaskFactory yet because we need the UI
+ // to be created first, so this is done in postCreateContent().
+ }
+
+ /**
+ * Once the UI has been created, initializes the content.
+ * This creates the pages, selects the first one, setup sources and scan for local folders.
+ *
+ * Returns true if we should show the window.
+ */
+ private boolean postCreateContent() {
+ ProgressViewFactory factory = new ProgressViewFactory();
+ factory.setProgressView(new ProgressView(
+ mStatusText, mProgressBar, mButtonStop));
+ mUpdaterData.setTaskFactory(factory);
+
+ setWindowImage(mShell);
+
+ setupSources();
+ initializeSettings();
+
+ if (mUpdaterData.checkIfInitFailed()) {
+ return false;
+ }
+
+ mUpdaterData.broadcastOnSdkLoaded();
+
+ if (mRequestAutoUpdate) {
+ mUpdaterData.updateOrInstallAll_WithGUI(
+ null /*selectedArchives*/,
+ false /* includeObsoletes */);
+ }
+
+ // Tell the one page its the selected one
+ mPkgPage.onPageSelected();
+
+ return true;
+ }
+
+ /**
+ * Creates the icon of the window shell.
+ */
+ private void setWindowImage(Shell androidSdkUpdater) {
+ String imageName = "android_icon_16.png"; //$NON-NLS-1$
+ if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) {
+ imageName = "android_icon_128.png"; //$NON-NLS-1$
+ }
+
+ if (mUpdaterData != null) {
+ ImageFactory imgFactory = mUpdaterData.getImageFactory();
+ if (imgFactory != null) {
+ mShell.setImage(imgFactory.getImageByName(imageName));
+ }
+ }
+ }
+
+ /**
+ * Called by the main loop when the window has been disposed.
+ */
+ private void dispose() {
+ mUpdaterData.getSources().saveUserAddons(mUpdaterData.getSdkLog());
+ }
+
+ /**
+ * Callback called when the window shell is disposed.
+ */
+ private void onAndroidSdkUpdaterDispose() {
+ if (mUpdaterData != null) {
+ ImageFactory imgFactory = mUpdaterData.getImageFactory();
+ if (imgFactory != null) {
+ imgFactory.dispose();
+ }
+ }
+ }
+
+ /**
+ * Used to initialize the sources.
+ */
+ private void setupSources() {
+ mUpdaterData.setupDefaultSources();
+ }
+
+ /**
+ * Initializes settings.
+ * This must be called after addExtraPages(), which created a settings page.
+ * Iterate through all the pages to find the first (and supposedly unique) setting page,
+ * and use it to load and apply these settings.
+ */
+ private void initializeSettings() {
+ SettingsController c = mUpdaterData.getSettingsController();
+ c.loadSettings();
+ c.applySettings();
+
+ // TODO give access to a settings dialog somehow (+about dialog)
+ // TODO c.setSettingsPage(settingsPage);
+ }
+
+ private void onToggleDetails() {
+ mButtonDetails.setState(1 - mButtonDetails.getState());
+ }
+
+ private void onStopSelected() {
+ // TODO
+ }
+
+ // End of hiding from SWT Designer
+ //$hide<<$
+
+ // -----
+
+ /**
+ * A label that can display 2 images depending on its internal state.
+ * This acts as a button by firing the {@link SWT#Selection} listener.
+ */
+ private static class ToggleButton extends CLabel {
+ private Image[] mImage = new Image[2];
+ private boolean mMouseIn;
+ private int mState = 0;
+
+
+ public ToggleButton(Composite parent, int style, Image image1, Image image2) {
+ super(parent, style);
+ mImage[0] = image1;
+ mImage[1] = image2;
+ updateImage();
+
+ addMouseListener(new MouseListener() {
+ public void mouseDown(MouseEvent e) {
+ // pass
+ }
+
+ public void mouseUp(MouseEvent e) {
+ // We select on mouse-up, as it should be properly done since this is the
+ // only way a user can cancel a button click by moving out of the button.
+ if (mMouseIn && e.button == 1) {
+ notifyListeners(SWT.Selection, new Event());
+ }
+ }
+
+ public void mouseDoubleClick(MouseEvent e) {
+ if (mMouseIn && e.button == 1) {
+ notifyListeners(SWT.DefaultSelection, new Event());
+ }
+ }
+ });
+
+ addMouseTrackListener(new MouseTrackListener() {
+ public void mouseExit(MouseEvent e) {
+ if (mMouseIn) {
+ mMouseIn = false;
+ redraw();
+ }
+ }
+
+ public void mouseEnter(MouseEvent e) {
+ if (!mMouseIn) {
+ mMouseIn = true;
+ redraw();
+ }
+ }
+
+ public void mouseHover(MouseEvent e) {
+ // pass
+ }
+ });
+ }
+
+ @Override
+ public int getStyle() {
+ int style = super.getStyle();
+ if (mMouseIn) {
+ style |= SWT.SHADOW_IN;
+ }
+ return style;
+ }
+
+ /**
+ * Sets current state.
+ * @param state A value 0 or 1.
+ */
+ public void setState(int state) {
+ assert state == 0 || state == 1;
+ mState = state;
+ updateImage();
+ redraw();
+ }
+
+ /**
+ * Returns the current state
+ * @return Returns the current state, either 0 or 1.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ protected void updateImage() {
+ setImage(mImage[getState()]);
+ }
+ }
+
+ /**
+ * A label that can display 2 images depending on its enabled/disabled state.
+ * This acts as a button by firing the {@link SWT#Selection} listener.
+ */
+ private static class ImgDisabledButton extends ToggleButton {
+ public ImgDisabledButton(Composite parent, int style,
+ Image imageEnabled, Image imageDisabled) {
+ super(parent, style, imageEnabled, imageDisabled);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ updateImage();
+ redraw();
+ }
+
+ @Override
+ public void setState(int state) {
+ throw new UnsupportedOperationException(); // not available for this type of button
+ }
+
+ @Override
+ public int getState() {
+ return (isDisposed() || !isEnabled()) ? 1 : 0;
+ }
+ }
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java
index 4a38f75..877ba37 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java
@@ -94,6 +94,10 @@ public class ImageFactory {
return null;
}
+ if (object instanceof Image) {
+ return (Image) object;
+ }
+
String clz = object.getClass().getSimpleName();
if (clz.endsWith(Package.class.getSimpleName())) {
String name = clz.replaceFirst(Package.class.getSimpleName(), "").toLowerCase() + //$NON-NLS-1$
@@ -122,6 +126,10 @@ public class ImageFactory {
}
}
+ if (object instanceof String) {
+ return getImageByName((String) object);
+ }
+
return null;
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.png
index acc124d..830c04b 100644
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.png
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/collapsed_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/collapsed_16.png
new file mode 100755
index 0000000..5f20b86
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/collapsed_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/expanded_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/expanded_16.png
new file mode 100755
index 0000000..6e13998
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/expanded_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon_16.png
index 147837f..147837f 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_installed_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_installed_16.png
new file mode 100755
index 0000000..78b7e5a
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_installed_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_new_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_new_16.png
new file mode 100755
index 0000000..0976ad4
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_new_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_update_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_update_16.png
new file mode 100755
index 0000000..e766251
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_update_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_16.png
new file mode 100755
index 0000000..cd9b807
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_other_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_other_16.png
new file mode 100755
index 0000000..395a240
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_other_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/status_ok_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/status_ok_16.png
new file mode 100755
index 0000000..eeb0a6f
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/status_ok_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_disabled_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_disabled_16.png
new file mode 100755
index 0000000..ae6da31
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_disabled_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_enabled_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_enabled_16.png
new file mode 100755
index 0000000..7ce1864
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_enabled_16.png
Binary files differ
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/IProgressUiProvider.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/IProgressUiProvider.java
new file mode 100755
index 0000000..4a7922d
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/IProgressUiProvider.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 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.sdkuilib.internal.tasks;
+
+import com.android.sdklib.internal.repository.ITaskMonitor;
+
+import org.eclipse.swt.widgets.ProgressBar;
+
+/**
+ * Interface for a user interface that displays both a task status
+ * (e.g. via an {@link ITaskMonitor}) and the progress state of the
+ * task (e.g. via a progress bar.)
+ * <p/>
+ * See {@link ITaskMonitor} for details on how a monitor expects to
+ * be displayed.
+ */
+interface IProgressUiProvider {
+
+ public abstract boolean isCancelRequested();
+
+ /**
+ * Sets the description in the current task dialog.
+ * This method can be invoked from a non-UI thread.
+ */
+ public abstract void setDescription(String description);
+
+ /**
+ * Logs a "normal" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public abstract void log(String log);
+
+ /**
+ * Logs an "error" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public abstract void logError(String log);
+
+ /**
+ * Logs a "verbose" information line, that is extra details which are typically
+ * not that useful for the end-user and might be hidden until explicitly shown.
+ * This method can be invoked from a non-UI thread.
+ */
+ public abstract void logVerbose(String log);
+
+ /**
+ * Sets the max value of the progress bar.
+ * This method can be invoked from a non-UI thread.
+ *
+ * @see ProgressBar#setMaximum(int)
+ */
+ public abstract void setProgressMax(int max);
+
+ /**
+ * Sets the current value of the progress bar.
+ * This method can be invoked from a non-UI thread.
+ */
+ public abstract void setProgress(int value);
+
+ /**
+ * Returns the current value of the progress bar,
+ * between 0 and up to {@link #setProgressMax(int)} - 1.
+ * This method can be invoked from a non-UI thread.
+ */
+ public abstract int getProgress();
+
+ /**
+ * Display a yes/no question dialog box.
+ *
+ * This implementation allow this to be called from any thread, it
+ * makes sure the dialog is opened synchronously in the ui thread.
+ *
+ * @param title The title of the dialog box
+ * @param message The error message
+ * @return true if YES was clicked.
+ */
+ public abstract boolean displayPrompt(String title, String message);
+
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java
index db2b781..42d5558 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java
@@ -19,23 +19,16 @@ package com.android.sdkuilib.internal.tasks;
import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskMonitor;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
/**
- * An {@link ITaskMonitor} that displays a {@link ProgressDialog}.
+ * An {@link ITaskMonitor} that displays a {@link ProgressTaskDialog}.
*/
-public final class ProgressTask implements ITaskMonitor {
+public final class ProgressTask extends TaskMonitorImpl {
- private static final double MAX_COUNT = 10000.0;
-
- private final ProgressDialog mDialog;
+ private final ProgressTaskDialog mDialog;
private boolean mAutomaticallyCloseOnTaskCompletion = true;
- private double mIncCoef = 0;
- private double mValue = 0;
/**
@@ -45,86 +38,16 @@ public final class ProgressTask implements ITaskMonitor {
* This blocks till the thread ends.
*/
public ProgressTask(Shell parent, String title, ITask task) {
- mDialog = new ProgressDialog(parent, createTaskThread(title, task));
+ super(new ProgressTaskDialog(parent));
+ mDialog = (ProgressTaskDialog) getUiProvider();
mDialog.setText(title);
- mDialog.open();
- }
-
- /**
- * Sets the description in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- public void setDescription(String descriptionFormat, Object...args) {
- mDialog.setDescription(descriptionFormat, args);
- }
-
- /**
- * Sets the description in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- public void setResult(String resultFormat, Object...args) {
- mAutomaticallyCloseOnTaskCompletion = false;
- mDialog.setResult(resultFormat, args);
- }
-
- /**
- * Sets the max value of the progress bar.
- * This method can be invoked from a non-UI thread.
- *
- * Weird things will happen if setProgressMax is called multiple times
- * *after* {@link #incProgress(int)}: we don't try to adjust it on the
- * fly.
- *
- * @see ProgressBar#setMaximum(int)
- */
- public void setProgressMax(int max) {
- assert max > 0;
- // Always set the dialog's progress max to 10k since it only handles
- // integers and we want to have a better inner granularity. Instead
- // we use the max to compute a coefficient for inc deltas.
- mDialog.setProgressMax((int) MAX_COUNT);
- mIncCoef = max > 0 ? MAX_COUNT / max : 0;
- assert mIncCoef > 0;
- }
-
- /**
- * Increments the current value of the progress bar.
- *
- * This method can be invoked from a non-UI thread.
- */
- public void incProgress(int delta) {
- if (delta > 0 && mIncCoef > 0) {
- internalIncProgress(delta * mIncCoef);
- }
- }
-
- private void internalIncProgress(double realDelta) {
- mValue += realDelta;
- mDialog.setProgress((int)mValue);
- }
-
- /**
- * Returns the current value of the progress bar,
- * between 0 and up to {@link #setProgressMax(int)} - 1.
- *
- * This method can be invoked from a non-UI thread.
- */
- public int getProgress() {
- assert mIncCoef > 0;
- return mIncCoef > 0 ? (int)(mDialog.getProgress() / mIncCoef) : 0;
- }
-
- /**
- * Returns true if the "Cancel" button was selected.
- * It is up to the task thread to pool this and exit.
- */
- public boolean isCancelRequested() {
- return mDialog.isCancelRequested();
+ mDialog.open(createTaskThread(title, task));
}
/**
* Creates a thread to run the task. The thread has not been started yet.
* When the task completes, requests to close the dialog.
+ *
* @return A new thread that will run the task. The thread has not been started yet.
*/
private Thread createTaskThread(String title, final ITask task) {
@@ -145,122 +68,12 @@ public final class ProgressTask implements ITaskMonitor {
}
/**
- * Display a yes/no question dialog box.
- *
- * This implementation allow this to be called from any thread, it
- * makes sure the dialog is opened synchronously in the ui thread.
- *
- * @param title The title of the dialog box
- * @param message The error message
- * @return true if YES was clicked.
+ * Sets the dialog to not auto-close since we want the user to see the error.
+ * {@inheritDoc}
*/
- public boolean displayPrompt(final String title, final String message) {
- final Shell shell = mDialog.getParent();
- Display display = shell.getDisplay();
-
- // we need to ask the user what he wants to do.
- final boolean[] result = new boolean[] { false };
- display.syncExec(new Runnable() {
- public void run() {
- result[0] = MessageDialog.openQuestion(shell, title, message);
- }
- });
- return result[0];
- }
-
- /**
- * Creates a sub-monitor that will use up to tickCount on the progress bar.
- * tickCount must be 1 or more.
- */
- public ITaskMonitor createSubMonitor(int tickCount) {
- assert mIncCoef > 0;
- assert tickCount > 0;
- return new SubTaskMonitor(this, null, mValue, tickCount * mIncCoef);
- }
-
- private interface ISubTaskMonitor extends ITaskMonitor {
- public void subIncProgress(double realDelta);
- }
-
- private static class SubTaskMonitor implements ISubTaskMonitor {
-
- private final ProgressTask mRoot;
- private final ISubTaskMonitor mParent;
- private final double mStart;
- private final double mSpan;
- private double mSubValue;
- private double mSubCoef;
-
- /**
- * Creates a new sub task monitor which will work for the given range [start, start+span]
- * in its parent.
- *
- * @param root The ProgressTask root
- * @param parent The immediate parent. Can be the null or another sub task monitor.
- * @param start The start value in the root's coordinates
- * @param span The span value in the root's coordinates
- */
- public SubTaskMonitor(ProgressTask root,
- ISubTaskMonitor parent,
- double start,
- double span) {
- mRoot = root;
- mParent = parent;
- mStart = start;
- mSpan = span;
- mSubValue = start;
- }
-
- public boolean isCancelRequested() {
- return mRoot.isCancelRequested();
- }
-
- public void setDescription(String descriptionFormat, Object... args) {
- mRoot.setDescription(descriptionFormat, args);
- }
-
- public void setResult(String resultFormat, Object... args) {
- mRoot.setResult(resultFormat, args);
- }
-
- public void setProgressMax(int max) {
- assert max > 0;
- mSubCoef = max > 0 ? mSpan / max : 0;
- assert mSubCoef > 0;
- }
-
- public int getProgress() {
- assert mSubCoef > 0;
- return mSubCoef > 0 ? (int)((mSubValue - mStart) / mSubCoef) : 0;
- }
-
- public void incProgress(int delta) {
- if (delta > 0 && mSubCoef > 0) {
- subIncProgress(delta * mSubCoef);
- }
- }
-
- public void subIncProgress(double realDelta) {
- mSubValue += realDelta;
- if (mParent != null) {
- mParent.subIncProgress(realDelta);
- } else {
- mRoot.internalIncProgress(realDelta);
- }
- }
-
- public boolean displayPrompt(String title, String message) {
- return mRoot.displayPrompt(title, message);
- }
-
- public ITaskMonitor createSubMonitor(int tickCount) {
- assert mSubCoef > 0;
- assert tickCount > 0;
- return new SubTaskMonitor(mRoot,
- this,
- mSubValue,
- tickCount * mSubCoef);
- }
+ @Override
+ public void logError(String format, Object...args) {
+ mAutomaticallyCloseOnTaskCompletion = false;
+ super.logError(format, args);
}
-
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java
index ff79c68..a4236fa 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressDialog.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java
@@ -19,9 +19,12 @@ package com.android.sdkuilib.internal.tasks;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.internal.repository.ITaskMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
@@ -34,18 +37,16 @@ import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
/**
- * Implements a {@link ProgressDialog}, used by the {@link ProgressTask} class.
+ * Implements a {@link ProgressTaskDialog}, used by the {@link ProgressTask} class.
* This separates the dialog UI from the task logic.
*
* Note: this does not implement the {@link ITaskMonitor} interface to avoid confusing
* SWT Designer.
*/
-final class ProgressDialog extends Dialog {
+final class ProgressTaskDialog extends Dialog implements IProgressUiProvider {
/**
* Min Y location for dialog. Need to deal with the menu bar on mac os.
@@ -79,29 +80,27 @@ final class ProgressDialog extends Dialog {
private ProgressBar mProgressBar;
private Button mCancelButton;
private Text mResultText;
- private final Thread mTaskThread;
/**
* Create the dialog.
* @param parent Parent container
- * @param taskThread The thread to run the task.
*/
- public ProgressDialog(Shell parent, Thread taskThread) {
+ public ProgressTaskDialog(Shell parent) {
super(parent, SWT.APPLICATION_MODAL);
- mTaskThread = taskThread;
}
/**
* Open the dialog and blocks till it gets closed
+ * @param taskThread The thread to run the task. Cannot be null.
*/
- public void open() {
+ public void open(Thread taskThread) {
createContents();
- positionShell(); //$hide$ (hide from SWT designer)
+ positionShell(); //$hide$ (hide from SWT designer)
mDialogShell.open();
mDialogShell.layout();
- startThread(); //$hide$ (hide from SWT designer)
+ startThread(taskThread); //$hide$ (hide from SWT designer)
Display display = getParent().getDisplay();
while (!mDialogShell.isDisposed() && mCancelMode != CancelMode.CLOSE_AUTO) {
@@ -273,41 +272,48 @@ final class ProgressDialog extends Dialog {
* Sets the description in the current task dialog.
* This method can be invoked from a non-UI thread.
*/
- public void setDescription(final String descriptionFormat, final Object...args) {
+ public void setDescription(final String description) {
mDialogShell.getDisplay().syncExec(new Runnable() {
public void run() {
if (!mLabel.isDisposed()) {
- mLabel.setText(String.format(descriptionFormat, args));
+ mLabel.setText(description);
}
}
});
}
/**
- * Sets the description in the current task dialog.
+ * Adds to the log in the current task dialog.
* This method can be invoked from a non-UI thread.
*/
- public void setResult(final String resultFormat, final Object...args) {
+ public void log(final String info) {
if (!mDialogShell.isDisposed()) {
mDialogShell.getDisplay().syncExec(new Runnable() {
public void run() {
if (!mResultText.isDisposed()) {
mResultText.setVisible(true);
- String newText = String.format(resultFormat, args);
String lastText = mResultText.getText();
if (lastText != null &&
lastText.length() > 0 &&
- !lastText.endsWith("\n") &&
- !newText.startsWith("\n")) {
- mResultText.append("\n");
+ !lastText.endsWith("\n") && //$NON-NLS-1$
+ !info.startsWith("\n")) { //$NON-NLS-1$
+ mResultText.append("\n"); //$NON-NLS-1$
}
- mResultText.append(newText);
+ mResultText.append(info);
}
}
});
}
}
+ public void logError(String info) {
+ log(info);
+ }
+
+ public void logVerbose(String info) {
+ log(info);
+ }
+
/**
* Sets the max value of the progress bar.
* This method can be invoked from a non-UI thread.
@@ -364,12 +370,35 @@ final class ProgressDialog extends Dialog {
}
/**
+ * Display a yes/no question dialog box.
+ *
+ * This implementation allow this to be called from any thread, it
+ * makes sure the dialog is opened synchronously in the ui thread.
+ *
+ * @param title The title of the dialog box
+ * @param message The error message
+ * @return true if YES was clicked.
+ */
+ public boolean displayPrompt(final String title, final String message) {
+ Display display = mDialogShell.getDisplay();
+
+ // we need to ask the user what he wants to do.
+ final boolean[] result = new boolean[] { false };
+ display.syncExec(new Runnable() {
+ public void run() {
+ result[0] = MessageDialog.openQuestion(mDialogShell, title, message);
+ }
+ });
+ return result[0];
+ }
+
+ /**
* Starts the thread that runs the task.
* This is deferred till the UI is created.
*/
- private void startThread() {
- if (mTaskThread != null) {
- mTaskThread.start();
+ private void startThread(Thread taskThread) {
+ if (taskThread != null) {
+ taskThread.start();
}
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java
new file mode 100755
index 0000000..9878c85
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2011 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.sdkuilib.internal.tasks;
+
+import com.android.sdklib.internal.repository.ITask;
+import com.android.sdklib.internal.repository.ITaskMonitor;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ProgressBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+
+/**
+ * Implements a "view" that uses an existing progress bar, status button and
+ * status text to display a {@link ITaskMonitor}.
+ */
+public final class ProgressView implements IProgressUiProvider {
+
+ private static enum State {
+ /** View created but there's no task running. Next state can only be ACTIVE. */
+ IDLE,
+ /** A task is currently running. Next state is either STOP_PENDING or IDLE. */
+ ACTIVE,
+ /** Stop button has been clicked. Waiting for thread to finish. Next state is IDLE. */
+ STOP_PENDING,
+ }
+
+ /** The current mode of operation of the dialog. */
+ private State mState = State.IDLE;
+
+ // UI fields
+ private final Label mLabel;
+ private final Control mStopButton;
+ private final ProgressBar mProgressBar;
+
+ /**
+ * Accumulated log text. This is intended to be displayed in a scrollable
+ * text area. The various methods that append to the log might not be called
+ * from the UI thread, so accesses should be synchronized on the builder.
+ */
+ private final StringBuilder mLogText = new StringBuilder();
+
+
+ /**
+ * Creates a new {@link ProgressView} object, a simple "holder" for the various
+ * widgets used to display and update a progress + status bar.
+ */
+ public ProgressView(Label label, ProgressBar progressBar, Control stopButton) {
+ mLabel = label;
+ mProgressBar = progressBar;
+ mProgressBar.setEnabled(false);
+
+ mStopButton = stopButton;
+ mStopButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ if (mState == State.ACTIVE) {
+ changeState(State.STOP_PENDING);
+ }
+ }
+ });
+ }
+
+ /**
+ * Starts the task and block till it's either finished or cancelled.
+ */
+ public void startTask(final String title, final ITask task) {
+ if (task != null) {
+ try {
+ mLabel.setText(title);
+ mProgressBar.setSelection(0);
+ mProgressBar.setEnabled(true);
+ changeState(ProgressView.State.ACTIVE);
+
+ Runnable r = new Runnable() {
+ public void run() {
+ task.run(new TaskMonitorImpl(ProgressView.this));
+ }
+ };
+
+ Thread t = new Thread(r, title);
+ t.start();
+
+ // Process the app's event loop whilst we wait for the thread to finish
+ Display display = mProgressBar.getDisplay();
+ while (!mProgressBar.isDisposed() && t.isAlive()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+
+
+ } catch (Exception e) {
+ // TODO log
+
+ } finally {
+ changeState(ProgressView.State.IDLE);
+ mProgressBar.setSelection(0);
+ mProgressBar.setEnabled(false);
+ }
+ }
+ }
+
+ private void syncExec(final Widget widget, final Runnable runnable) {
+ if (widget != null && !widget.isDisposed()) {
+ widget.getDisplay().syncExec(runnable);
+ }
+ }
+
+ private void changeState(State state) {
+ if (mState != null ) {
+ mState = state;
+ }
+
+ syncExec(mStopButton, new Runnable() {
+ public void run() {
+ mStopButton.setEnabled(mState == State.ACTIVE);
+ }
+ });
+
+ }
+
+ // --- Implementation of ITaskUiProvider ---
+
+ public boolean isCancelRequested() {
+ return mState != State.ACTIVE;
+ }
+
+ /**
+ * Sets the description in the current task dialog.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void setDescription(final String description) {
+ syncExec(mLabel, new Runnable() {
+ public void run() {
+ mLabel.setText(description);
+ }
+ });
+ synchronized (mLogText) {
+ mLogText.append("** ").append(description);
+ }
+ }
+
+ /**
+ * Logs a "normal" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void log(String log) {
+ synchronized (mLogText) {
+ mLogText.append("=> ").append(log);
+ }
+ }
+
+ /**
+ * Logs an "error" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logError(String log) {
+ synchronized (mLogText) {
+ mLogText.append("=> ").append(log);
+ }
+ }
+
+ /**
+ * Logs a "verbose" information line, that is extra details which are typically
+ * not that useful for the end-user and might be hidden until explicitly shown.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logVerbose(String log) {
+ synchronized (mLogText) {
+ mLogText.append("=> ").append(log);
+ }
+ }
+
+ /**
+ * Sets the max value of the progress bar.
+ * This method can be invoked from a non-UI thread.
+ *
+ * @see ProgressBar#setMaximum(int)
+ */
+ public void setProgressMax(final int max) {
+ syncExec(mProgressBar, new Runnable() {
+ public void run() {
+ mProgressBar.setMaximum(max);
+ }
+ });
+ }
+
+ /**
+ * Sets the current value of the progress bar.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void setProgress(final int value) {
+ syncExec(mProgressBar, new Runnable() {
+ public void run() {
+ mProgressBar.setSelection(value);
+ }
+ });
+ }
+
+ /**
+ * Returns the current value of the progress bar,
+ * between 0 and up to {@link #setProgressMax(int)} - 1.
+ * This method can be invoked from a non-UI thread.
+ */
+ public int getProgress() {
+ final int[] result = new int[] { 0 };
+
+ if (!mProgressBar.isDisposed()) {
+ mProgressBar.getDisplay().syncExec(new Runnable() {
+ public void run() {
+ if (!mProgressBar.isDisposed()) {
+ result[0] = mProgressBar.getSelection();
+ }
+ }
+ });
+ }
+
+ return result[0];
+ }
+
+ public boolean displayPrompt(final String title, final String message) {
+ final boolean[] result = new boolean[] { false };
+
+ if (!mProgressBar.isDisposed()) {
+ final Shell shell = mProgressBar.getShell();
+ Display display = shell.getDisplay();
+
+ display.syncExec(new Runnable() {
+ public void run() {
+ result[0] = MessageDialog.openQuestion(shell, title, message);
+ }
+ });
+ }
+
+ return result[0];
+ }
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java
new file mode 100755
index 0000000..448b478
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java
@@ -0,0 +1,41 @@
+/*
+ * 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.sdkuilib.internal.tasks;
+
+import com.android.sdklib.internal.repository.ITask;
+import com.android.sdklib.internal.repository.ITaskFactory;
+
+/**
+ * An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog
+ * for each new task.
+ */
+public final class ProgressViewFactory implements ITaskFactory {
+
+ private ProgressView mProgressView;
+
+ public ProgressViewFactory() {
+ }
+
+ public void setProgressView(ProgressView progressView) {
+ mProgressView = progressView;
+ }
+
+ public void start(String title, ITask task) {
+ assert mProgressView != null;
+ mProgressView.startTask(title, task);
+ }
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java
new file mode 100755
index 0000000..c95db47
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2011 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.sdkuilib.internal.tasks;
+
+import com.android.sdklib.internal.repository.ITaskMonitor;
+
+import org.eclipse.swt.widgets.ProgressBar;
+
+/**
+ * Internal class that implements the logic of an {@link ITaskMonitor}.
+ * It doesn't deal with any UI directly. Instead it delegates the UI to
+ * the provided {@link IProgressUiProvider}.
+ */
+class TaskMonitorImpl implements ITaskMonitor {
+
+ private static final double MAX_COUNT = 10000.0;
+
+ private interface ISubTaskMonitor extends ITaskMonitor {
+ public void subIncProgress(double realDelta);
+ }
+
+ private double mIncCoef = 0;
+ private double mValue = 0;
+ private final IProgressUiProvider mUi;
+
+ /**
+ * Constructs a new {@link TaskMonitorImpl} that relies on the given
+ * {@link IProgressUiProvider} to change the user interface.
+ * @param ui The {@link IProgressUiProvider}. Cannot be null.
+ */
+ public TaskMonitorImpl(IProgressUiProvider ui) {
+ mUi = ui;
+ }
+
+ /** Returns the {@link IProgressUiProvider} passed to the constructor. */
+ public IProgressUiProvider getUiProvider() {
+ return mUi;
+ }
+
+ /**
+ * Sets the description in the current task dialog.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void setDescription(String format, Object... args) {
+ final String text = String.format(format, args);
+ mUi.setDescription(text);
+ }
+
+ /**
+ * Logs a "normal" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void log(String format, Object... args) {
+ String text = String.format(format, args);
+ mUi.log(text);
+ }
+
+ /**
+ * Logs an "error" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logError(String format, Object... args) {
+ String text = String.format(format, args);
+ mUi.logError(text);
+ }
+
+ /**
+ * Logs a "verbose" information line, that is extra details which are typically
+ * not that useful for the end-user and might be hidden until explicitly shown.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logVerbose(String format, Object... args) {
+ String text = String.format(format, args);
+ mUi.logVerbose(text);
+ }
+
+ /**
+ * Sets the max value of the progress bar.
+ * This method can be invoked from a non-UI thread.
+ *
+ * Weird things will happen if setProgressMax is called multiple times
+ * *after* {@link #incProgress(int)}: we don't try to adjust it on the
+ * fly.
+ *
+ * @see ProgressBar#setMaximum(int)
+ */
+ public void setProgressMax(int max) {
+ assert max > 0;
+ // Always set the dialog's progress max to 10k since it only handles
+ // integers and we want to have a better inner granularity. Instead
+ // we use the max to compute a coefficient for inc deltas.
+ mUi.setProgressMax((int) MAX_COUNT);
+ mIncCoef = max > 0 ? MAX_COUNT / max : 0;
+ assert mIncCoef > 0;
+ }
+
+ /**
+ * Increments the current value of the progress bar.
+ *
+ * This method can be invoked from a non-UI thread.
+ */
+ public void incProgress(int delta) {
+ if (delta > 0 && mIncCoef > 0) {
+ internalIncProgress(delta * mIncCoef);
+ }
+ }
+
+ private void internalIncProgress(double realDelta) {
+ mValue += realDelta;
+ mUi.setProgress((int)mValue);
+ }
+
+ /**
+ * Returns the current value of the progress bar,
+ * between 0 and up to {@link #setProgressMax(int)} - 1.
+ *
+ * This method can be invoked from a non-UI thread.
+ */
+ public int getProgress() {
+ // mIncCoef is 0 if setProgressMax hasn't been used yet.
+ return mIncCoef > 0 ? (int)(mUi.getProgress() / mIncCoef) : 0;
+ }
+
+ /**
+ * Returns true if the "Cancel" button was selected.
+ * It is up to the task thread to pool this and exit.
+ */
+ public boolean isCancelRequested() {
+ return mUi.isCancelRequested();
+ }
+
+ /**
+ * Display a yes/no question dialog box.
+ *
+ * This implementation allow this to be called from any thread, it
+ * makes sure the dialog is opened synchronously in the ui thread.
+ *
+ * @param title The title of the dialog box
+ * @param message The error message
+ * @return true if YES was clicked.
+ */
+ public boolean displayPrompt(final String title, final String message) {
+ return mUi.displayPrompt(title, message);
+ }
+
+ /**
+ * Creates a sub-monitor that will use up to tickCount on the progress bar.
+ * tickCount must be 1 or more.
+ */
+ public ITaskMonitor createSubMonitor(int tickCount) {
+ assert mIncCoef > 0;
+ assert tickCount > 0;
+ return new SubTaskMonitor(this, null, mValue, tickCount * mIncCoef);
+ }
+
+ private static class SubTaskMonitor implements ISubTaskMonitor {
+
+ private final TaskMonitorImpl mRoot;
+ private final ISubTaskMonitor mParent;
+ private final double mStart;
+ private final double mSpan;
+ private double mSubValue;
+ private double mSubCoef;
+
+ /**
+ * Creates a new sub task monitor which will work for the given range [start, start+span]
+ * in its parent.
+ *
+ * @param taskMonitor The ProgressTask root
+ * @param parent The immediate parent. Can be the null or another sub task monitor.
+ * @param start The start value in the root's coordinates
+ * @param span The span value in the root's coordinates
+ */
+ public SubTaskMonitor(TaskMonitorImpl taskMonitor,
+ ISubTaskMonitor parent,
+ double start,
+ double span) {
+ mRoot = taskMonitor;
+ mParent = parent;
+ mStart = start;
+ mSpan = span;
+ mSubValue = start;
+ }
+
+ public boolean isCancelRequested() {
+ return mRoot.isCancelRequested();
+ }
+
+ public void setDescription(String format, Object... args) {
+ mRoot.setDescription(format, args);
+ }
+
+ public void log(String format, Object... args) {
+ mRoot.log(format, args);
+ }
+
+ public void logError(String format, Object... args) {
+ mRoot.logError(format, args);
+ }
+
+ public void logVerbose(String format, Object... args) {
+ mRoot.logVerbose(format, args);
+ }
+
+ public void setProgressMax(int max) {
+ assert max > 0;
+ mSubCoef = max > 0 ? mSpan / max : 0;
+ assert mSubCoef > 0;
+ }
+
+ public int getProgress() {
+ assert mSubCoef > 0;
+ return mSubCoef > 0 ? (int)((mSubValue - mStart) / mSubCoef) : 0;
+ }
+
+ public void incProgress(int delta) {
+ if (delta > 0 && mSubCoef > 0) {
+ subIncProgress(delta * mSubCoef);
+ }
+ }
+
+ public void subIncProgress(double realDelta) {
+ mSubValue += realDelta;
+ if (mParent != null) {
+ mParent.subIncProgress(realDelta);
+ } else {
+ mRoot.internalIncProgress(realDelta);
+ }
+ }
+
+ public boolean displayPrompt(String title, String message) {
+ return mRoot.displayPrompt(title, message);
+ }
+
+ public ITaskMonitor createSubMonitor(int tickCount) {
+ assert mSubCoef > 0;
+ assert tickCount > 0;
+ return new SubTaskMonitor(mRoot,
+ this,
+ mSubValue,
+ tickCount * mSubCoef);
+ }
+ }
+
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java
index 4411034..0932378 100644
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java
@@ -16,20 +16,21 @@
package com.android.sdkuilib.internal.widgets;
-import com.android.prefs.AndroidLocation;
+import com.android.io.FileWrapper;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
+import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdklib.internal.avd.HardwareProperties;
+import com.android.sdklib.internal.avd.AvdManager.AvdConflict;
import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty;
import com.android.sdklib.internal.project.ProjectProperties;
-import com.android.sdklib.io.FileWrapper;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.ui.GridDialog;
+import com.android.util.Pair;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.CellEditor;
@@ -73,8 +74,8 @@ import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.TreeMap;
+import java.util.Map.Entry;
import java.util.regex.Matcher;
/**
@@ -97,11 +98,18 @@ final class AvdCreationDialog extends GridDialog {
private final ArrayList<String> mEditedProperties = new ArrayList<String>();
private final ImageFactory mImageFactory;
private final ISdkLog mSdkLog;
+ /**
+ * The original AvdInfo if we're editing an existing AVD.
+ * Null when we're creating a new AVD.
+ */
private final AvdInfo mEditAvdInfo;
private Text mAvdName;
private Combo mTargetCombo;
+ private Combo mAbiTypeCombo;
+ private String mAbiType;
+
private Button mSdCardSizeRadio;
private Text mSdCardSize;
private Combo mSdCardSizeCombo;
@@ -153,8 +161,11 @@ final class AvdCreationDialog extends GridDialog {
public void modifyText(ModifyEvent e) {
String name = mAvdName.getText().trim();
if (mEditAvdInfo == null || !name.equals(mEditAvdInfo.getName())) {
- AvdInfo avdMatch = mAvdManager.getAvd(name, false /*validAvdOnly*/);
- if (avdMatch != null) {
+ // Case where we're creating a new AVD or editing an existing one
+ // and the AVD name has been changed... check for name uniqueness.
+
+ Pair<AvdConflict, String> conflict = mAvdManager.isAvdNameConflicting(name);
+ if (conflict.getFirst() != AvdManager.AvdConflict.NO_CONFLICT) {
// If we're changing the state from disabled to enabled, make sure
// to uncheck the button, to force the user to voluntarily re-enforce it.
// This happens when editing an existing AVD and changing the name from
@@ -168,8 +179,10 @@ final class AvdCreationDialog extends GridDialog {
mForceCreation.setSelection(false);
}
} else {
+ // Case where we're editing an existing AVD with the name unchanged.
+
mForceCreation.setEnabled(false);
- mForceCreation.setSelection(true);
+ mForceCreation.setSelection(false);
}
validatePage();
}
@@ -285,10 +298,29 @@ final class AvdCreationDialog extends GridDialog {
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
reloadSkinCombo();
+ reloadAbiTypeCombo();
validatePage();
}
});
+ //ABI group
+ label = new Label(parent, SWT.NONE);
+ label.setText("ABI:");
+ tooltip = "The ABI to use in the virtual device";
+ label.setToolTipText(tooltip);
+
+ mAbiTypeCombo = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
+ mAbiTypeCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mAbiTypeCombo.setToolTipText(tooltip);
+ mAbiTypeCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ validatePage();
+ }
+ });
+ mAbiTypeCombo.setEnabled(false);
+
// --- sd card group
label = new Label(parent, SWT.NONE);
label.setText("SD Card:");
@@ -322,6 +354,7 @@ final class AvdCreationDialog extends GridDialog {
mSdCardSizeCombo = new Combo(sdCardGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
mSdCardSizeCombo.add("KiB");
mSdCardSizeCombo.add("MiB");
+ mSdCardSizeCombo.add("GiB");
mSdCardSizeCombo.select(1);
mSdCardSizeCombo.addSelectionListener(validateListener);
@@ -668,80 +701,102 @@ final class AvdCreationDialog extends GridDialog {
for (int i = 0;i < n; i++) {
if (target.equals(mCurrentTargets.get(mTargetCombo.getItem(i)))) {
mTargetCombo.select(i);
+ reloadAbiTypeCombo();
reloadSkinCombo();
break;
}
}
}
- Map<String, String> props = mEditAvdInfo.getProperties();
-
- // First try the skin name and if it doesn't work fallback on the skin path
- nextSkin: for (int s = 0; s < 2; s++) {
- String skin = props.get(s == 0 ? AvdManager.AVD_INI_SKIN_NAME
- : AvdManager.AVD_INI_SKIN_PATH);
- if (skin != null && skin.length() > 0) {
- Matcher m = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin);
- if (m.matches() && m.groupCount() == 2) {
- enableSkinWidgets(false);
- mSkinListRadio.setSelection(false);
- mSkinSizeRadio.setSelection(true);
- mSkinSizeWidth.setText(m.group(1));
- mSkinSizeHeight.setText(m.group(2));
- break nextSkin;
- } else {
- enableSkinWidgets(true);
- mSkinSizeRadio.setSelection(false);
- mSkinListRadio.setSelection(true);
+ // select the abi type
+ if (target != null && target.getAbiList().length > 0) {
+ mAbiTypeCombo.setEnabled(target.getAbiList().length > 1);
+ String abiType = AvdInfo.getPrettyAbiType(mEditAvdInfo.getAbiType());
+ int n = mAbiTypeCombo.getItemCount();
+ for (int i = 0; i < n; i++) {
+ if (abiType.equals(mAbiTypeCombo.getItem(i))) {
+ mAbiTypeCombo.select(i);
+ reloadSkinCombo();
+ break;
+ }
+ }
+ }
- int n = mSkinCombo.getItemCount();
- for (int i = 0; i < n; i++) {
- if (skin.equals(mSkinCombo.getItem(i))) {
- mSkinCombo.select(i);
- break nextSkin;
+ Map<String, String> props = mEditAvdInfo.getProperties();
+ if (props != null) {
+ // First try the skin name and if it doesn't work fallback on the skin path
+ nextSkin: for (int s = 0; s < 2; s++) {
+ String skin = props.get(s == 0 ? AvdManager.AVD_INI_SKIN_NAME
+ : AvdManager.AVD_INI_SKIN_PATH);
+ if (skin != null && skin.length() > 0) {
+ Matcher m = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin);
+ if (m.matches() && m.groupCount() == 2) {
+ enableSkinWidgets(false);
+ mSkinListRadio.setSelection(false);
+ mSkinSizeRadio.setSelection(true);
+ mSkinSizeWidth.setText(m.group(1));
+ mSkinSizeHeight.setText(m.group(2));
+ break nextSkin;
+ } else {
+ enableSkinWidgets(true);
+ mSkinSizeRadio.setSelection(false);
+ mSkinListRadio.setSelection(true);
+
+ int n = mSkinCombo.getItemCount();
+ for (int i = 0; i < n; i++) {
+ if (skin.equals(mSkinCombo.getItem(i))) {
+ mSkinCombo.select(i);
+ break nextSkin;
+ }
}
}
}
}
- }
- String sdcard = props.get(AvdManager.AVD_INI_SDCARD_PATH);
- if (sdcard != null && sdcard.length() > 0) {
- enableSdCardWidgets(false);
- mSdCardSizeRadio.setSelection(false);
- mSdCardFileRadio.setSelection(true);
- mSdCardFile.setText(sdcard);
- }
+ String sdcard = props.get(AvdManager.AVD_INI_SDCARD_PATH);
+ if (sdcard != null && sdcard.length() > 0) {
+ enableSdCardWidgets(false);
+ mSdCardSizeRadio.setSelection(false);
+ mSdCardFileRadio.setSelection(true);
+ mSdCardFile.setText(sdcard);
+ }
+
+ sdcard = props.get(AvdManager.AVD_INI_SDCARD_SIZE);
+ if (sdcard != null && sdcard.length() > 0) {
+ String[] values = new String[2];
+ long sdcardSize = AvdManager.parseSdcardSize(sdcard, values);
+
+ if (sdcardSize != AvdManager.SDCARD_NOT_SIZE_PATTERN) {
+ enableSdCardWidgets(true);
+ mSdCardFileRadio.setSelection(false);
+ mSdCardSizeRadio.setSelection(true);
+
+ mSdCardSize.setText(values[0]);
- sdcard = props.get(AvdManager.AVD_INI_SDCARD_SIZE);
- if (sdcard != null && sdcard.length() > 0) {
- Matcher m = AvdManager.SDCARD_SIZE_PATTERN.matcher(sdcard);
- if (m.matches() && m.groupCount() == 2) {
- enableSdCardWidgets(true);
- mSdCardFileRadio.setSelection(false);
- mSdCardSizeRadio.setSelection(true);
-
- mSdCardSize.setText(m.group(1));
-
- String suffix = m.group(2);
- int n = mSdCardSizeCombo.getItemCount();
- for (int i = 0; i < n; i++) {
- if (mSdCardSizeCombo.getItem(i).startsWith(suffix)) {
- mSdCardSizeCombo.select(i);
+ String suffix = values[1];
+ int n = mSdCardSizeCombo.getItemCount();
+ for (int i = 0; i < n; i++) {
+ if (mSdCardSizeCombo.getItem(i).startsWith(suffix)) {
+ mSdCardSizeCombo.select(i);
+ }
}
}
}
- }
- String snapshots = props.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT);
- if (snapshots != null && snapshots.length() > 0) {
- mSnapshotCheck.setSelection(snapshots.equals("true"));
+ String snapshots = props.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT);
+ if (snapshots != null && snapshots.length() > 0) {
+ mSnapshotCheck.setSelection(snapshots.equals("true"));
+ }
}
mProperties.clear();
- mProperties.putAll(props);
+
+ if (props != null) {
+ mProperties.putAll(props);
+ }
// Cleanup known non-hardware properties
+ mProperties.remove(AvdManager.AVD_INI_ABI_TYPE);
mProperties.remove(AvdManager.AVD_INI_SKIN_PATH);
mProperties.remove(AvdManager.AVD_INI_SKIN_NAME);
mProperties.remove(AvdManager.AVD_INI_SDCARD_SIZE);
@@ -882,6 +937,49 @@ final class AvdCreationDialog extends GridDialog {
}
/**
+ * Reload all the abi types in the selection list
+ */
+ private void reloadAbiTypeCombo() {
+ String selected = null;
+ boolean found = false;
+
+ int index = mTargetCombo.getSelectionIndex();
+ if (index >= 0) {
+ String targetName = mTargetCombo.getItem(index);
+ IAndroidTarget target = mCurrentTargets.get(targetName);
+ String[] arches = target.getAbiList();
+
+ mAbiTypeCombo.setEnabled(arches.length > 1);
+
+ // If user explicitly selected an ABI before, preserve that option
+ // If user did not explicitly select before (only one option before)
+ // force them to select
+ index = mAbiTypeCombo.getSelectionIndex();
+ if (index >= 0 && mAbiTypeCombo.getItemCount() > 1) {
+ selected = mAbiTypeCombo.getItem(index);
+ }
+
+ mAbiTypeCombo.removeAll();
+
+ int i;
+ for ( i = 0; i < arches.length ; i++ ) {
+ String prettyAbiType = AvdInfo.getPrettyAbiType(arches[i]);
+ mAbiTypeCombo.add(prettyAbiType);
+ if (!found) {
+ found = prettyAbiType.equals(selected);
+ if (found) {
+ mAbiTypeCombo.select(i);
+ }
+ }
+ }
+
+ if (arches.length == 1) {
+ mAbiTypeCombo.select(0);
+ }
+ }
+ }
+
+ /**
* Validates the fields, displays errors and warnings.
* Enables the finish button if there are no errors.
*/
@@ -905,6 +1003,16 @@ final class AvdCreationDialog extends GridDialog {
error = "A target must be selected in order to create an AVD.";
}
+ // validate abi type if the selected target supports multi archs.
+ if (hasAvdName && error == null && mTargetCombo.getSelectionIndex() > 0) {
+ int index = mTargetCombo.getSelectionIndex();
+ String targetName = mTargetCombo.getItem(index);
+ IAndroidTarget target = mCurrentTargets.get(targetName);
+ if (target.getAbiList().length > 1 && mAbiTypeCombo.getSelectionIndex() < 0) {
+ error = "An abi type must be selected in order to create an AVD.";
+ }
+ }
+
// Validate SDCard path or value
if (error == null) {
// get the mode. We only need to check the file since the
@@ -918,29 +1026,40 @@ final class AvdCreationDialog extends GridDialog {
} else {
String valueString = mSdCardSize.getText();
if (valueString.length() > 0) {
- // prevent overflow: no more than 999GB
- // 10 digit for MiB, 13 for KiB
- if (valueString.length() >= 10 +
- (mSdCardSizeCombo.getSelectionIndex() == 0 ? 3 : 0)) {
- error = "SD Card size is too big!";
- } else {
- try {
- long value = Long.parseLong(valueString);
-
- switch (mSdCardSizeCombo.getSelectionIndex()) {
- case 0:
- value *= 1024L;
- break;
- case 1:
- value *= 1024L * 1024L;
- break;
- }
+ long value = 0;
+ try {
+ value = Long.parseLong(valueString);
+
+ int sizeIndex = mSdCardSizeCombo.getSelectionIndex();
+ if (sizeIndex >= 0) {
+ // index 0 shifts by 10 (1024=K), index 1 by 20, etc.
+ value <<= 10*(1 + sizeIndex);
+ }
- if (value < 9 * 1024 * 1024) {
- error = "SD Card size must be at least 9 MiB";
+ if (value < AvdManager.SDCARD_MIN_BYTE_SIZE ||
+ value > AvdManager.SDCARD_MAX_BYTE_SIZE) {
+ value = 0;
+ }
+ } catch (Exception e) {
+ // ignore, we'll test value below.
+ }
+ if (value <= 0) {
+ error = "SD Card size is invalid. Range is 9 MiB..1023 GiB.";
+ } else if (mEditAvdInfo != null) {
+ // When editing an existing AVD, compare with the existing
+ // sdcard size, if any. It only matters if there was an sdcard setting
+ // before.
+ Map<String, String> props = mEditAvdInfo.getProperties();
+ if (props != null) {
+ String original =
+ mEditAvdInfo.getProperties().get(AvdManager.AVD_INI_SDCARD_SIZE);
+ if (original != null && original.length() > 0) {
+ long originalSize =
+ AvdManager.parseSdcardSize(original, null/*parsedStrings*/);
+ if (originalSize > 0 && value != originalSize) {
+ warning = "A new SD Card file will be created.\nThe current SD Card file will be lost.";
+ }
}
- } catch (NumberFormatException e) {
- // will never happen thanks to the VerifyListener.
}
}
}
@@ -957,24 +1076,47 @@ final class AvdCreationDialog extends GridDialog {
String height = mSkinSizeHeight.getText(); // rejects non digit.
if (width.length() == 0 || height.length() == 0) {
- error = "Skin size is incorrect.\nBoth dimensions must be > 0";
+ error = "Skin size is incorrect.\nBoth dimensions must be > 0.";
}
}
}
// Check for duplicate AVD name
- if (isCreate && hasAvdName && error == null) {
- AvdInfo avdMatch = mAvdManager.getAvd(avdName, false /*validAvdOnly*/);
- if (avdMatch != null && !mForceCreation.getSelection()) {
+ if (isCreate && hasAvdName && error == null && !mForceCreation.getSelection()) {
+ Pair<AvdConflict, String> conflict = mAvdManager.isAvdNameConflicting(avdName);
+ assert conflict != null;
+ switch(conflict.getFirst()) {
+ case NO_CONFLICT:
+ break;
+ case CONFLICT_EXISTING_AVD:
+ case CONFLICT_INVALID_AVD:
error = String.format(
"The AVD name '%s' is already used.\n" +
"Check \"Override the existing AVD\" to delete the existing one.",
avdName);
+ break;
+ case CONFLICT_EXISTING_PATH:
+ error = String.format(
+ "Conflict with %s\n" +
+ "Check \"Override the existing AVD\" to delete the existing one.",
+ conflict.getSecond());
+ break;
+ default:
+ // Hmm not supposed to happen... probably someone expanded the
+ // enum without adding something here. In this case just do an
+ // assert and use a generic error message.
+ error = String.format(
+ "Conflict %s with %s.\n" +
+ "Check \"Override the existing AVD\" to delete the existing one.",
+ conflict.getFirst().toString(),
+ conflict.getSecond());
+ assert false;
+ break;
}
}
if (error == null && mEditAvdInfo != null && isCreate) {
- warning = String.format("The AVD '%1$s' will be duplicated into '%2$s'",
+ warning = String.format("The AVD '%1$s' will be duplicated into '%2$s'.",
mEditAvdInfo.getName(),
avdName);
}
@@ -1102,6 +1244,19 @@ final class AvdCreationDialog extends GridDialog {
return false;
}
+ // get the abi type
+ mAbiType = SdkConstants.ABI_ARMEABI;
+ if (target.getAbiList().length > 0) {
+ int abiIndex = mAbiTypeCombo.getSelectionIndex();
+ if (abiIndex >= 0) {
+ String prettyname = mAbiTypeCombo.getItem(abiIndex);
+ //Extract the abi type
+ int firstIndex = prettyname.indexOf("(");
+ int lastIndex = prettyname.indexOf(")");
+ mAbiType = prettyname.substring(firstIndex+1, lastIndex);
+ }
+ }
+
// get the SD card data from the UI.
String sdName = null;
if (mSdCardSizeRadio.getSelection()) {
@@ -1112,10 +1267,13 @@ final class AvdCreationDialog extends GridDialog {
// add the unit
switch (mSdCardSizeCombo.getSelectionIndex()) {
case 0:
- sdName += "K";
+ sdName += "K"; //$NON-NLS-1$
break;
case 1:
- sdName += "M";
+ sdName += "M"; //$NON-NLS-1$
+ break;
+ case 2:
+ sdName += "G"; //$NON-NLS-1$
break;
default:
// shouldn't be here
@@ -1154,9 +1312,7 @@ final class AvdCreationDialog extends GridDialog {
File avdFolder = null;
try {
- avdFolder = new File(
- AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD,
- avdName + AvdManager.AVD_FOLDER_EXTENSION);
+ avdFolder = AvdInfo.getDefaultAvdFolder(mAvdManager, avdName);
} catch (AndroidLocationException e) {
return false;
}
@@ -1169,11 +1325,13 @@ final class AvdCreationDialog extends GridDialog {
avdFolder,
avdName,
target,
+ mAbiType,
skinName,
sdName,
mProperties,
- force,
snapshot,
+ force,
+ mEditAvdInfo != null, //edit existing
log);
success = avdInfo != null;
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java
index 409c25d..6a85c14 100644
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java
@@ -18,9 +18,9 @@ package com.android.sdkuilib.internal.widgets;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus;
+import com.android.sdklib.internal.avd.AvdInfo.AvdStatus;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
@@ -101,7 +101,9 @@ final class AvdDetailsDialog extends Dialog {
if (mAvdInfo != null) {
displayValue(c, "Name:", mAvdInfo.getName());
- displayValue(c, "Path:", mAvdInfo.getPath());
+ displayValue(c, "ABI:", AvdInfo.getPrettyAbiType(mAvdInfo.getAbiType()));
+
+ displayValue(c, "Path:", mAvdInfo.getDataFolderPath());
if (mAvdInfo.getStatus() != AvdStatus.OK) {
displayValue(c, "Error:", mAvdInfo.getErrorMessage());
@@ -135,6 +137,7 @@ final class AvdDetailsDialog extends Dialog {
// display other hardware
HashMap<String, String> copy = new HashMap<String, String>(properties);
// remove stuff we already displayed (or that we don't want to display)
+ copy.remove(AvdManager.AVD_INI_ABI_TYPE);
copy.remove(AvdManager.AVD_INI_SKIN_NAME);
copy.remove(AvdManager.AVD_INI_SKIN_PATH);
copy.remove(AvdManager.AVD_INI_SDCARD_SIZE);
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java
index 5e26a41..56f2c7e 100644
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java
@@ -20,15 +20,15 @@ import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.NullSdkLog;
-import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus;
+import com.android.sdklib.internal.avd.AvdInfo.AvdStatus;
import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskMonitor;
import com.android.sdkuilib.internal.repository.SettingsController;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.internal.tasks.ProgressTask;
+import com.android.sdkuilib.repository.IUpdaterWindow;
import com.android.sdkuilib.repository.UpdaterWindow;
import org.eclipse.jface.dialogs.MessageDialog;
@@ -372,8 +372,10 @@ public final class AvdSelector {
column2.setText("Platform");
final TableColumn column3 = new TableColumn(mTable, SWT.NONE);
column3.setText("API Level");
+ final TableColumn column4 = new TableColumn(mTable, SWT.NONE);
+ column4.setText("ABI");
- adjustColumnsWidth(mTable, column0, column1, column2, column3);
+ adjustColumnsWidth(mTable, column0, column1, column2, column3, column4);
setupSelectionListener(mTable);
fillTable(mTable);
setEnabled(true);
@@ -633,16 +635,18 @@ public final class AvdSelector {
final TableColumn column0,
final TableColumn column1,
final TableColumn column2,
- final TableColumn column3) {
+ final TableColumn column3,
+ final TableColumn column4) {
// Add a listener to resize the column to the full width of the table
table.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Rectangle r = table.getClientArea();
- column0.setWidth(r.width * 25 / 100); // 25%
- column1.setWidth(r.width * 45 / 100); // 45%
+ column0.setWidth(r.width * 20 / 100); // 20%
+ column1.setWidth(r.width * 30 / 100); // 30%
column2.setWidth(r.width * 15 / 100); // 15%
column3.setWidth(r.width * 15 / 100); // 15%
+ column4.setWidth(r.width * 20 / 100); // 22%
}
});
}
@@ -777,10 +781,12 @@ public final class AvdSelector {
item.setText(1, target.getFullName());
item.setText(2, target.getVersionName());
item.setText(3, target.getVersion().getApiString());
+ item.setText(4, AvdInfo.getPrettyAbiType(avd.getAbiType()));
} else {
item.setText(1, "?");
item.setText(2, "?");
item.setText(3, "?");
+ item.setText(4, "?");
}
}
}
@@ -1003,7 +1009,7 @@ public final class AvdSelector {
log = new MessageBoxLog("Result of SDK Manager", display, true /*logErrorsOnly*/);
}
- UpdaterWindow window = new UpdaterWindow(
+ IUpdaterWindow window = new UpdaterWindow(
mTable.getShell(),
log,
mAvdManager.getSdkManager().getLocation());
@@ -1025,10 +1031,7 @@ public final class AvdSelector {
AvdStartDialog dialog = new AvdStartDialog(mTable.getShell(), avdInfo, mOsSdkPath,
mController);
if (dialog.open() == Window.OK) {
- String path = mOsSdkPath +
- File.separator +
- SdkConstants.OS_SDK_TOOLS_FOLDER +
- SdkConstants.FN_EMULATOR;
+ String path = avdInfo.getEmulatorPath(mOsSdkPath + File.separator);
final String avdName = avdInfo.getName();
@@ -1070,7 +1073,8 @@ public final class AvdSelector {
new ITask() {
public void run(ITaskMonitor monitor) {
try {
- monitor.setDescription("Starting emulator for AVD '%1$s'",
+ monitor.setDescription(
+ "Starting emulator for AVD '%1$s'",
avdName);
int n = 10;
monitor.setProgressMax(n);
@@ -1092,7 +1096,7 @@ public final class AvdSelector {
}
}
} catch (IOException e) {
- monitor.setResult("Failed to start emulator: %1$s",
+ monitor.logError("Failed to start emulator: %1$s",
e.getMessage());
}
}
@@ -1119,7 +1123,7 @@ public final class AvdSelector {
while (true) {
String line = errReader.readLine();
if (line != null) {
- monitor.setResult("%1$s", line); //$NON-NLS-1$
+ monitor.logError("%1$s", line); //$NON-NLS-1$
} else {
break;
}
@@ -1140,7 +1144,7 @@ public final class AvdSelector {
while (true) {
String line = outReader.readLine();
if (line != null) {
- monitor.setResult("%1$s", line); //$NON-NLS-1$
+ monitor.log("%1$s", line); //$NON-NLS-1$
} else {
break;
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java
index 77f47d1..7731dc1 100644
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java
@@ -16,8 +16,8 @@
package com.android.sdkuilib.internal.widgets;
+import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.internal.repository.SettingsController;
import com.android.sdkuilib.ui.GridDialog;
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java
index d5c1818..89edb2f 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java
@@ -95,7 +95,15 @@ public final class MessageBoxLog implements ISdkLog {
if (logMessages.size() > 0) {
final StringBuilder sb = new StringBuilder(mMessage + "\n\n");
for (String msg : logMessages) {
- sb.append(msg);
+ if (msg.length() > 0) {
+ if (msg.charAt(0) != '\n') {
+ int n = sb.length();
+ if (n > 0 && sb.charAt(n-1) != '\n') {
+ sb.append('\n');
+ }
+ }
+ sb.append(msg);
+ }
}
// display the message
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/IUpdaterWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/IUpdaterWindow.java
new file mode 100755
index 0000000..a771e53
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/IUpdaterWindow.java
@@ -0,0 +1,68 @@
+/*
+ * 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.sdkuilib.repository;
+
+import org.eclipse.swt.widgets.Composite;
+
+public interface IUpdaterWindow {
+
+ /**
+ * Registers an extra page for the updater window.
+ * <p/>
+ * Pages must derive from {@link Composite} and implement a constructor that takes
+ * a single parent {@link Composite} argument.
+ * <p/>
+ * All pages must be registered before the call to {@link #open()}.
+ *
+ * @param title The title of the page.
+ * @param pageClass The {@link Composite}-derived class that will implement the page.
+ */
+ public abstract void registerPage(String title,
+ Class<? extends Composite> pageClass);
+
+ /**
+ * Indicate the initial page that should be selected when the window opens.
+ * <p/>
+ * This must be called before the call to {@link #open()}.
+ * If null or if the page class is not found, the first page will be selected.
+ */
+ public abstract void setInitialPage(Class<? extends Composite> pageClass);
+
+ /**
+ * Sets whether the auto-update wizard will be shown when opening the window.
+ * <p/>
+ * This must be called before the call to {@link #open()}.
+ */
+ public abstract void setRequestAutoUpdate(boolean requestAutoUpdate);
+
+ /**
+ * Adds a new listener to be notified when a change is made to the content of the SDK.
+ */
+ public abstract void addListener(ISdkChangeListener listener);
+
+ /**
+ * Removes a new listener to be notified anymore when a change is made to the content of
+ * the SDK.
+ */
+ public abstract void removeListener(ISdkChangeListener listener);
+
+ /**
+ * Opens the window.
+ */
+ public abstract void open();
+
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java
index c78b08c..d49b072 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java
@@ -18,6 +18,7 @@ package com.android.sdkuilib.repository;
import com.android.sdklib.ISdkLog;
import com.android.sdkuilib.internal.repository.UpdaterWindowImpl;
+import com.android.sdkuilib.internal.repository.UpdaterWindowImpl2;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
@@ -27,9 +28,9 @@ import org.eclipse.swt.widgets.Shell;
*
* This is the public interface for using the window.
*/
-public class UpdaterWindow {
+public class UpdaterWindow implements IUpdaterWindow {
- private UpdaterWindowImpl mWindow;
+ private IUpdaterWindow mWindow;
/**
* Creates a new window. Caller must call open(), which will block.
@@ -39,7 +40,13 @@ public class UpdaterWindow {
* @param osSdkRoot The OS path to the SDK root.
*/
public UpdaterWindow(Shell parentShell, ISdkLog sdkLog, String osSdkRoot) {
- mWindow = new UpdaterWindowImpl(parentShell, sdkLog, osSdkRoot);
+
+ // TODO right now the new PackagesPage is experimental and not enabled by default
+ if (System.getenv("ANDROID_SDKMAN_EXP") != null) { //$NON-NLS-1$
+ mWindow = new UpdaterWindowImpl2(parentShell, sdkLog, osSdkRoot);
+ } else {
+ mWindow = new UpdaterWindowImpl(parentShell, sdkLog, osSdkRoot);
+ }
}
/**
@@ -54,7 +61,7 @@ public class UpdaterWindow {
* @param pageClass The {@link Composite}-derived class that will implement the page.
*/
public void registerPage(String title, Class<? extends Composite> pageClass) {
- mWindow.registerExtraPage(title, pageClass);
+ mWindow.registerPage(title, pageClass);
}
/**
diff --git a/anttasks/src/Android.mk b/sdkmanager/libs/sdkuilib/tests/Android.mk
index 5cefb37..a7ba9a7 100644
--- a/anttasks/src/Android.mk
+++ b/sdkmanager/libs/sdkuilib/tests/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
+# Copyright (C) 2011 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.
@@ -12,18 +11,22 @@
# 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)
+# Only compile source java files in this lib.
LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_JAR_MANIFEST := ../etc/manifest.txt
-LOCAL_JAVA_LIBRARIES := \
- sdklib \
- ant
+LOCAL_MODULE := sdkuilib-tests
+LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := anttasks
+LOCAL_JAVA_LIBRARIES := \
+ sdklib \
+ sdklib-tests \
+ sdkuilib \
+ junit \
+ swt \
include $(BUILD_HOST_JAVA_LIBRARY)
-
diff --git a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java
index 185589f..9470f91 100755
--- a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java
+++ b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java
@@ -31,7 +31,6 @@ import com.android.sdklib.mock.MockLog;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.List;
import java.util.Properties;
@@ -187,7 +186,7 @@ public class UpdaterDataTest extends TestCase {
return false;
}
- public void setDescription(String descriptionFormat, Object... args) {
+ public void setDescription(String format, Object... args) {
// ignore
}
@@ -195,7 +194,15 @@ public class UpdaterDataTest extends TestCase {
// ignore
}
- public void setResult(String resultFormat, Object... args) {
+ public void log(String format, Object... args) {
+ // ignore
+ }
+
+ public void logError(String format, Object... args) {
+ // ignore
+ }
+
+ public void logVerbose(String format, Object... args) {
// ignore
}
}
@@ -242,6 +249,11 @@ public class UpdaterDataTest extends TestCase {
}
@Override
+ public String getListDescription() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
public String getShortDescription() {
return this.getClass().getSimpleName();
}
diff --git a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java
index 72229ff..84241d4 100755
--- a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java
+++ b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java
@@ -21,16 +21,13 @@ import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.repository.Archive;
import com.android.sdklib.internal.repository.ITaskFactory;
-import com.android.sdklib.internal.repository.ITaskMonitor;
import com.android.sdklib.internal.repository.MockAddonPackage;
import com.android.sdklib.internal.repository.MockBrokenPackage;
import com.android.sdklib.internal.repository.MockPlatformPackage;
import com.android.sdklib.internal.repository.MockPlatformToolPackage;
import com.android.sdklib.internal.repository.MockToolPackage;
import com.android.sdklib.internal.repository.Package;
-import com.android.sdklib.internal.repository.SdkRepoSource;
import com.android.sdklib.internal.repository.SdkSource;
-import com.android.sdklib.internal.repository.SdkSourceCategory;
import com.android.sdklib.internal.repository.SdkSources;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
diff --git a/sdkstats/NOTICE b/sdkstats/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/sdkstats/NOTICE
@@ -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/swtmenubar/.classpath b/swtmenubar/.classpath
new file mode 100644
index 0000000..0f69dc5
--- /dev/null
+++ b/swtmenubar/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/swtmenubar/.gitignore b/swtmenubar/.gitignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/swtmenubar/.gitignore
@@ -0,0 +1 @@
+bin
diff --git a/swtmenubar/.project b/swtmenubar/.project
new file mode 100644
index 0000000..484282a
--- /dev/null
+++ b/swtmenubar/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>SwtMenuBar</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sdkmanager/libs/sdklib/src/Android.mk b/swtmenubar/Android.mk
index 37eda21..25e80da 100644
--- a/sdkmanager/libs/sdklib/src/Android.mk
+++ b/swtmenubar/Android.mk
@@ -1,11 +1,11 @@
#
-# Copyright (C) 2008 The Android Open Source Project
+# Copyright (C) 2011 The Android Open Source Project
#
-# Licensed under the Apache License, Version 2.0 (the "License");
+# Licensed under the Eclipse Public License, Version 1.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# 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,
@@ -16,15 +16,21 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_JAVA_RESOURCE_DIRS := .
-LOCAL_JAR_MANIFEST := ../manifest.txt
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+ifeq ($(HOST_OS),darwin)
+LOCAL_SRC_FILES += $(call all-java-files-under, src-$(HOST_OS))
+LOCAL_JAVA_RESOURCE_DIRS += src-$(HOST_OS)
+endif
+
+LOCAL_MODULE := swtmenubar
+LOCAL_MODULE_TAGS := optional
+
LOCAL_JAVA_LIBRARIES := \
- androidprefs \
- common \
- commons-compress-1.0
+ swt \
+ org.eclipse.jface_3.4.2.M20090107-0800
-LOCAL_MODULE := sdklib
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/swtmenubar/MODULE_LICENSE_EPL b/swtmenubar/MODULE_LICENSE_EPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/swtmenubar/MODULE_LICENSE_EPL
diff --git a/swtmenubar/NOTICE b/swtmenubar/NOTICE
new file mode 100644
index 0000000..49c101d
--- /dev/null
+++ b/swtmenubar/NOTICE
@@ -0,0 +1,224 @@
+*Eclipse Public License - v 1.0*
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
+THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+*1. DEFINITIONS*
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and
+documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and
+are distributed by that particular Contributor. A Contribution
+'originates' from a Contributor if it was added to the Program by such
+Contributor itself or anyone acting on such Contributor's behalf.
+Contributions do not include additions to the Program which: (i) are
+separate modules of software distributed in conjunction with the Program
+under their own license agreement, and (ii) are not derivative works of
+the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which
+are necessarily infringed by the use or sale of its Contribution alone
+or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+*2. GRANT OF RIGHTS*
+
+a) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free copyright
+license to reproduce, prepare derivative works of, publicly display,
+publicly perform, distribute and sublicense the Contribution of such
+Contributor, if any, and such derivative works, in source code and
+object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free patent license
+under Licensed Patents to make, use, sell, offer to sell, import and
+otherwise transfer the Contribution of such Contributor, if any, in
+source code and object code form. This patent license shall apply to the
+combination of the Contribution and the Program if, at the time the
+Contribution is added by the Contributor, such addition of the
+Contribution causes such combination to be covered by the Licensed
+Patents. The patent license shall not apply to any other combinations
+which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the
+licenses to its Contributions set forth herein, no assurances are
+provided by any Contributor that the Program does not infringe the
+patent or other intellectual property rights of any other entity. Each
+Contributor disclaims any liability to Recipient for claims brought by
+any other entity based on infringement of intellectual property rights
+or otherwise. As a condition to exercising the rights and licenses
+granted hereunder, each Recipient hereby assumes sole responsibility to
+secure any other intellectual property rights needed, if any. For
+example, if a third party patent license is required to allow Recipient
+to distribute the Program, it is Recipient's responsibility to acquire
+that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient
+copyright rights in its Contribution, if any, to grant the copyright
+license set forth in this Agreement.
+
+*3. REQUIREMENTS*
+
+A Contributor may choose to distribute the Program in object code form
+under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties
+and conditions, express and implied, including warranties or conditions
+of title and non-infringement, and implied warranties or conditions of
+merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for
+damages, including direct, indirect, special, incidental and
+consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are
+offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such
+Contributor, and informs licensees how to obtain it in a reasonable
+manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+*4. COMMERCIAL DISTRIBUTION*
+
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While this
+license is intended to facilitate the commercial use of the Program, the
+Contributor who includes the Program in a commercial product offering
+should do so in a manner which does not create potential liability for
+other Contributors. Therefore, if a Contributor includes the Program in
+a commercial product offering, such Contributor ("Commercial
+Contributor") hereby agrees to defend and indemnify every other
+Contributor ("Indemnified Contributor") against any losses, damages and
+costs (collectively "Losses") arising from claims, lawsuits and other
+legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the
+Program in a commercial product offering. The obligations in this
+section do not apply to any claims or Losses relating to any actual or
+alleged intellectual property infringement. In order to qualify, an
+Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial
+Contributor to control, and cooperate with the Commercial Contributor
+in, the defense and any related settlement negotiations. The Indemnified
+Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.
+
+*5. NO WARRANTY*
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED
+ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
+OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR
+A PARTICULAR PURPOSE. Each Recipient is solely responsible for
+determining the appropriateness of using and distributing the Program
+and assumes all risks associated with its exercise of rights under this
+Agreement , including but not limited to the risks and costs of program
+errors, compliance with applicable laws, damage to or loss of data,
+programs or equipment, and unavailability or interruption of operations.
+
+*6. DISCLAIMER OF LIABILITY*
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+*7. GENERAL*
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further action
+by the parties hereto, such provision shall be reformed to the minimum
+extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including
+a cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails
+to comply with any of the material terms or conditions of this Agreement
+and does not cure such failure in a reasonable period of time after
+becoming aware of such noncompliance. If all Recipient's rights under
+this Agreement terminate, Recipient agrees to cease use and distribution
+of the Program as soon as reasonably practicable. However, Recipient's
+obligations under this Agreement and any licenses granted by Recipient
+relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and may
+only be modified in the following manner. The Agreement Steward reserves
+the right to publish new versions (including revisions) of this
+Agreement from time to time. No one other than the Agreement Steward has
+the right to modify this Agreement. The Eclipse Foundation is the
+initial Agreement Steward. The Eclipse Foundation may assign the
+responsibility to serve as the Agreement Steward to a suitable separate
+entity. Each new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+distributed subject to the version of the Agreement under which it was
+received. In addition, after a new version of the Agreement is
+published, Contributor may elect to distribute the Program (including
+its Contributions) under the new version. Except as expressly stated in
+Sections 2(a) and 2(b) above, Recipient receives no rights or licenses
+to the intellectual property of any Contributor under this Agreement,
+whether expressly, by implication, estoppel or otherwise. All rights in
+the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to
+this Agreement will bring a legal action under this Agreement more than
+one year after the cause of action arose. Each party waives its rights
+to a jury trial in any resulting litigation.
+
+
+
diff --git a/swtmenubar/README b/swtmenubar/README
new file mode 100755
index 0000000..ba7c25a
--- /dev/null
+++ b/swtmenubar/README
@@ -0,0 +1,80 @@
+Using the Eclipse project SwtMenuBar
+------------------------------------
+
+This project provides a platform-specific way to hook into
+the default OS menu bar.
+
+On MacOS, it allows an SWT app to have an About menu item
+and to hook into the default Preferences menu item.
+
+On Windows and Linux, an SWT Menu should be provided (typically
+named "Tools") into which the About and Options menu items
+will be added.
+
+
+Consequently the implementation contains platform-specific source
+folders for the Java files that rely on a platform-specific version
+of SWT.jar.
+
+Right now we have the following source folders:
+- src/ - Generic implementation for all platforms.
+- src-darwin/ - Implementation for MacOS Carbon.
+
+*Only* the default "src/" folder is declared in the project .classpath
+so that the project can be opened in Eclipse on any platform and still
+work. However that means that on MacOS the custom src-darwin folder is
+not used by default.
+
+
+
+1- To build the library:
+
+Do not use Eclipse to build the library. Instead use the makefile:
+
+$ cd $TOP_OF_ANDROID_TREE
+$ . build/envsetup.sh && lunch sdk-eng
+$ make swtmenubar
+
+This will create a Jar in <Android tree>/out/host/<platform>/framework/
+that can then be included in the target application.
+
+
+2- To use the library in a target application:
+
+Build the swtmenubar library as explained in step 1.
+
+In the target application, define a classpath variable in Eclipse:
+- Open Preferences > Java > Build Path > Classpath Variables
+- Create a new classpath variable named ANDROID_OUT_FRAMEWORK
+- Set its folder value to <Android tree>/out/host/<platform>/framework
+
+Then add a variable to the Build Path of the target project:
+- Open Project > Properties > Java Build Path
+- Select the "Libraries" tab
+- Use "Add Variable"
+- Select ANDROID_OUT_FRAMEWORK
+- Select "Extend..."
+- Select swtmenubar.jar (which you previously built at step 1)
+
+
+3- Tip for developing this library:
+
+Keep in mind that src-darwin folder must not be added to the
+source folder list, otherwise the library would not compile
+on Windows or Linux.
+
+If you change anything to IMenuBarCallback, make sure to test
+on a Mac to be sure you're not breaking the API.
+
+To work on this on a Mac, you can either:
+a- simply temporarily add src-darwin as a source folder to the
+ build path and remove it before submitting.
+b- or directly edit the java files and rebuild the library using
+ 'make swtmenubar' from a shell.
+
+To test the library, use 'make swtmenubar'. This will build the
+library in out/... and the sdkmanager project is already setup
+to find it there.
+
+--
+EOF
diff --git a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java
new file mode 100755
index 0000000..45dacfb
--- /dev/null
+++ b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 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.menubar.internal;
+
+import com.android.menubar.IMenuBarCallback;
+import com.android.menubar.IMenuBarEnhancer;
+
+import org.eclipse.swt.internal.Callback;
+import org.eclipse.swt.internal.carbon.HICommand;
+import org.eclipse.swt.internal.carbon.OS;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+
+
+/**
+ * Implementation of IMenuBarEnhancer for MacOS Carbon SWT.
+ */
+public final class MenuBarEnhancerCarbon implements IMenuBarEnhancer {
+
+ private static final int kHICommandPreferences = ('p'<<24) + ('r'<<16) + ('e'<<8) + 'f';
+ private static final int kHICommandAbout = ('a'<<24) + ('b'<<16) + ('o'<<8) + 'u';
+ private static final int kHICommandServices = ('s'<<24) + ('e'<<16) + ('r'<<8) + 'v';
+
+ public MenuBarEnhancerCarbon() {
+ }
+
+ public MenuBarMode getMenuBarMode() {
+ return MenuBarMode.MAC_OS;
+ }
+
+ public void setupMenu(
+ String appName,
+ Display display,
+ final IMenuBarCallback callbacks) {
+
+ // Callback target
+ Object target = new Object() {
+ @SuppressWarnings("unused")
+ int commandProc(int nextHandler, int theEvent, int userData) {
+ if (OS.GetEventKind(theEvent) == OS.kEventProcessCommand) {
+ HICommand command = new HICommand();
+ OS.GetEventParameter(
+ theEvent,
+ OS.kEventParamDirectObject,
+ OS.typeHICommand,
+ null,
+ HICommand.sizeof,
+ null,
+ command);
+ switch (command.commandID) {
+ case kHICommandPreferences:
+ callbacks.onPreferencesMenuSelected();
+ return OS.eventNotHandledErr; // TODO wrong
+ case kHICommandAbout:
+ callbacks.onAboutMenuSelected();
+ return OS.eventNotHandledErr;// TODO wrong
+ default:
+ break;
+ }
+ }
+ return OS.eventNotHandledErr;
+ }
+ };
+
+ final Callback commandCallback= new Callback(target, "commandProc", 3); //$NON-NLS-1$
+ int commandProc = commandCallback.getAddress();
+ if (commandProc == 0) {
+ commandCallback.dispose();
+ log(callbacks, "%1$s: commandProc hook failed.", getClass().getSimpleName()); //$NON-NLS-1$
+ return; // give up
+ }
+
+ // Install event handler for commands
+ int[] mask = new int[] {
+ OS.kEventClassCommand, OS.kEventProcessCommand
+ };
+ OS.InstallEventHandler(
+ OS.GetApplicationEventTarget(), commandProc, mask.length / 2, mask, 0, null);
+
+ // create About Eclipse menu command
+ int[] outMenu = new int[1];
+ short[] outIndex = new short[1];
+ if (OS.GetIndMenuItemWithCommandID(
+ 0, kHICommandPreferences, 1, outMenu, outIndex) == OS.noErr && outMenu[0] != 0) {
+ int menu = outMenu[0];
+
+ // add About menu item (which isn't present by default)
+ String about = "About " + appName;
+ int l = about.length();
+ char buffer[] = new char[l];
+ about.getChars(0, l, buffer, 0);
+ int str = OS.CFStringCreateWithCharacters(OS.kCFAllocatorDefault, buffer, l);
+ OS.InsertMenuItemTextWithCFString(menu, str, (short) 0, 0, kHICommandAbout);
+ OS.CFRelease(str);
+
+ // add separator between About & Preferences
+ OS.InsertMenuItemTextWithCFString(menu, 0, (short) 1, OS.kMenuItemAttrSeparator, 0);
+
+ // enable pref menu
+ OS.EnableMenuCommand(menu, kHICommandPreferences);
+
+ // disable services menu
+ OS.DisableMenuCommand(menu, kHICommandServices);
+ }
+
+ // schedule disposal of callback object
+ display.disposeExec(
+ new Runnable() {
+ public void run() {
+ commandCallback.dispose();
+ }
+ }
+ );
+ }
+
+ private void log(IMenuBarCallback callbacks, String format, Object... args) {
+ callbacks.printError(format , args);
+ }
+
+}
diff --git a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java
new file mode 100644
index 0000000..170603a
--- /dev/null
+++ b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2011 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.
+ *
+ * History:
+ * Original code by the <a href="http://www.simidude.com/blog/2008/macify-a-swt-application-in-a-cross-platform-way/">CarbonUIEnhancer from Agynami</a>
+ * with the implementation being modified from the <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.ui.cocoa/src/org/eclipse/ui/internal/cocoa/CocoaUIEnhancer.java">org.eclipse.ui.internal.cocoa.CocoaUIEnhancer</a>,
+ * then modified by http://www.transparentech.com/opensource/cocoauienhancer to use reflection
+ * rather than 'link' to SWT cocoa, and finally modified to be usable by the SwtMenuBar project.
+ */
+
+package com.android.menubar.internal;
+
+import com.android.menubar.IMenuBarCallback;
+import com.android.menubar.IMenuBarEnhancer;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.internal.C;
+import org.eclipse.swt.internal.Callback;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class MenuBarEnhancerCocoa implements IMenuBarEnhancer {
+
+ private static final long kAboutMenuItem = 0;
+ private static final long kPreferencesMenuItem = 2;
+ // private static final long kServicesMenuItem = 4;
+ // private static final long kHideApplicationMenuItem = 6;
+ private static final long kQuitMenuItem = 10;
+
+ static long mSelPreferencesMenuItemSelected;
+ static long mSelAboutMenuItemSelected;
+ static Callback mProc3Args;
+
+ private String mAppName;
+
+ /**
+ * Class invoked via the Callback object to run the about and preferences
+ * actions.
+ * <p>
+ * If you don't use JFace in your application (SWT only), change the
+ * {@link org.eclipse.jface.action.IAction}s to
+ * {@link org.eclipse.swt.widgets.Listener}s.
+ * </p>
+ */
+ private static class ActionProctarget {
+ private final IMenuBarCallback mCallbacks;
+
+ public ActionProctarget(IMenuBarCallback callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ /**
+ * Will be called on 32bit SWT.
+ */
+ @SuppressWarnings("unused")
+ public int actionProc(int id, int sel, int arg0) {
+ return (int) actionProc((long) id, (long) sel, (long) arg0);
+ }
+
+ /**
+ * Will be called on 64bit SWT.
+ */
+ public long actionProc(long id, long sel, long arg0) {
+ if (sel == mSelAboutMenuItemSelected) {
+ mCallbacks.onAboutMenuSelected();
+ } else if (sel == mSelPreferencesMenuItemSelected) {
+ mCallbacks.onPreferencesMenuSelected();
+ } else {
+ // Unknown selection!
+ }
+ // Return value is not used.
+ return 0;
+ }
+ }
+
+ /**
+ * Construct a new CocoaUIEnhancer.
+ *
+ * @param mAppName The name of the application. It will be used to customize
+ * the About and Quit menu items. If you do not wish to customize
+ * the About and Quit menu items, just pass <tt>null</tt> here.
+ */
+ public MenuBarEnhancerCocoa() {
+ }
+
+ public MenuBarMode getMenuBarMode() {
+ return MenuBarMode.MAC_OS;
+ }
+
+ /**
+ * Setup the About and Preferences native menut items with the
+ * given application name and links them to the callback.
+ *
+ * @param appName The application name.
+ * @param display The SWT display. Must not be null.
+ * @param callbacks The callbacks invoked by the menus.
+ */
+ public void setupMenu(
+ String appName,
+ Display display,
+ IMenuBarCallback callbacks) {
+
+ mAppName = appName;
+
+ // This is our callback object whose 'actionProc' method will be called
+ // when the About or Preferences menuItem is invoked.
+ ActionProctarget target = new ActionProctarget(callbacks);
+
+ try {
+ // Initialize the menuItems.
+ initialize(target);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+
+ // Schedule disposal of callback object
+ display.disposeExec(new Runnable() {
+ public void run() {
+ invoke(mProc3Args, "dispose");
+ }
+ });
+ }
+
+ private void initialize(Object callbackObject)
+ throws Exception {
+
+ Class<?> osCls = classForName("org.eclipse.swt.internal.cocoa.OS");
+
+ // Register names in objective-c.
+ if (mSelAboutMenuItemSelected == 0) {
+ mSelPreferencesMenuItemSelected = registerName(osCls, "preferencesMenuItemSelected:"); //$NON-NLS-1$
+ mSelAboutMenuItemSelected = registerName(osCls, "aboutMenuItemSelected:"); //$NON-NLS-1$
+ }
+
+ // Create an SWT Callback object that will invoke the actionProc method
+ // of our internal callback Object.
+ mProc3Args = new Callback(callbackObject, "actionProc", 3); //$NON-NLS-1$
+ Method getAddress = Callback.class.getMethod("getAddress", new Class[0]);
+ Object object = getAddress.invoke(mProc3Args, (Object[]) null);
+ long proc3 = convertToLong(object);
+ if (proc3 == 0) {
+ SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
+ }
+
+ Class<?> nsMenuCls = classForName("org.eclipse.swt.internal.cocoa.NSMenu");
+ Class<?> nsMenuitemCls = classForName("org.eclipse.swt.internal.cocoa.NSMenuItem");
+ Class<?> nsStringCls = classForName("org.eclipse.swt.internal.cocoa.NSString");
+ Class<?> nsApplicationCls = classForName("org.eclipse.swt.internal.cocoa.NSApplication");
+
+ // Instead of creating a new delegate class in objective-c,
+ // just use the current SWTApplicationDelegate. An instance of this
+ // is a field of the Cocoa Display object and is already the target
+ // for the menuItems. So just get this class and add the new methods
+ // to it.
+ object = invoke(osCls, "objc_lookUpClass", new Object[] {
+ "SWTApplicationDelegate"
+ });
+ long cls = convertToLong(object);
+
+ // Add the action callbacks for Preferences and About menu items.
+ invoke(osCls, "class_addMethod",
+ new Object[] {
+ wrapPointer(cls),
+ wrapPointer(mSelPreferencesMenuItemSelected),
+ wrapPointer(proc3), "@:@"}); //$NON-NLS-1$
+ invoke(osCls, "class_addMethod",
+ new Object[] {
+ wrapPointer(cls),
+ wrapPointer(mSelAboutMenuItemSelected),
+ wrapPointer(proc3), "@:@"}); //$NON-NLS-1$
+
+ // Get the Mac OS X Application menu.
+ Object sharedApplication = invoke(nsApplicationCls, "sharedApplication");
+ Object mainMenu = invoke(sharedApplication, "mainMenu");
+ Object mainMenuItem = invoke(nsMenuCls, mainMenu, "itemAtIndex", new Object[] {
+ wrapPointer(0)
+ });
+ Object appMenu = invoke(mainMenuItem, "submenu");
+
+ // Create the About <application-name> menu command
+ Object aboutMenuItem =
+ invoke(nsMenuCls, appMenu, "itemAtIndex", new Object[] {
+ wrapPointer(kAboutMenuItem)
+ });
+ if (mAppName != null) {
+ Object nsStr = invoke(nsStringCls, "stringWith", new Object[] {
+ "About " + mAppName
+ });
+ invoke(nsMenuitemCls, aboutMenuItem, "setTitle", new Object[] {
+ nsStr
+ });
+ }
+ // Rename the quit action.
+ if (mAppName != null) {
+ Object quitMenuItem =
+ invoke(nsMenuCls, appMenu, "itemAtIndex", new Object[] {
+ wrapPointer(kQuitMenuItem)
+ });
+ Object nsStr = invoke(nsStringCls, "stringWith", new Object[] {
+ "Quit " + mAppName
+ });
+ invoke(nsMenuitemCls, quitMenuItem, "setTitle", new Object[] {
+ nsStr
+ });
+ }
+
+ // Enable the Preferences menuItem.
+ Object prefMenuItem =
+ invoke(nsMenuCls, appMenu, "itemAtIndex", new Object[] {
+ wrapPointer(kPreferencesMenuItem)
+ });
+ invoke(nsMenuitemCls, prefMenuItem, "setEnabled", new Object[] {
+ true
+ });
+
+ // Set the action to execute when the About or Preferences menuItem is
+ // invoked.
+ //
+ // We don't need to set the target here as the current target is the
+ // SWTApplicationDelegate and we have registerd the new selectors on
+ // it. So just set the new action to invoke the selector.
+ invoke(nsMenuitemCls, prefMenuItem, "setAction",
+ new Object[] {
+ wrapPointer(mSelPreferencesMenuItemSelected)
+ });
+ invoke(nsMenuitemCls, aboutMenuItem, "setAction",
+ new Object[] {
+ wrapPointer(mSelAboutMenuItemSelected)
+ });
+ }
+
+ private long registerName(Class<?> osCls, String name)
+ throws IllegalArgumentException, SecurityException, IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
+ Object object = invoke(osCls, "sel_registerName", new Object[] {
+ name
+ });
+ return convertToLong(object);
+ }
+
+ private long convertToLong(Object object) {
+ if (object instanceof Integer) {
+ Integer i = (Integer) object;
+ return i.longValue();
+ }
+ if (object instanceof Long) {
+ Long l = (Long) object;
+ return l.longValue();
+ }
+ return 0;
+ }
+
+ private static Object wrapPointer(long value) {
+ Class<?> PTR_CLASS = C.PTR_SIZEOF == 8 ? long.class : int.class;
+ if (PTR_CLASS == long.class) {
+ return new Long(value);
+ } else {
+ return new Integer((int) value);
+ }
+ }
+
+ private static Object invoke(Class<?> clazz, String methodName, Object[] args) {
+ return invoke(clazz, null, methodName, args);
+ }
+
+ private static Object invoke(Class<?> clazz, Object target, String methodName, Object[] args) {
+ try {
+ Class<?>[] signature = new Class<?>[args.length];
+ for (int i = 0; i < args.length; i++) {
+ Class<?> thisClass = args[i].getClass();
+ if (thisClass == Integer.class)
+ signature[i] = int.class;
+ else if (thisClass == Long.class)
+ signature[i] = long.class;
+ else if (thisClass == Byte.class)
+ signature[i] = byte.class;
+ else if (thisClass == Boolean.class)
+ signature[i] = boolean.class;
+ else
+ signature[i] = thisClass;
+ }
+ Method method = clazz.getMethod(methodName, signature);
+ return method.invoke(target, args);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private Class<?> classForName(String classname) {
+ try {
+ Class<?> cls = Class.forName(classname);
+ return cls;
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private Object invoke(Class<?> cls, String methodName) {
+ return invoke(cls, methodName, (Class<?>[]) null, (Object[]) null);
+ }
+
+ private Object invoke(Class<?> cls, String methodName, Class<?>[] paramTypes,
+ Object... arguments) {
+ try {
+ Method m = cls.getDeclaredMethod(methodName, paramTypes);
+ return m.invoke(null, arguments);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private Object invoke(Object obj, String methodName) {
+ return invoke(obj, methodName, (Class<?>[]) null, (Object[]) null);
+ }
+
+ private Object invoke(Object obj, String methodName, Class<?>[] paramTypes, Object... arguments) {
+ try {
+ Method m = obj.getClass().getDeclaredMethod(methodName, paramTypes);
+ return m.invoke(obj, arguments);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/swtmenubar/src/com/android/menubar/IMenuBarCallback.java b/swtmenubar/src/com/android/menubar/IMenuBarCallback.java
new file mode 100644
index 0000000..b0d6568
--- /dev/null
+++ b/swtmenubar/src/com/android/menubar/IMenuBarCallback.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 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.menubar;
+
+
+
+/**
+ * Callbacks used by {@link IMenuBarEnhancer}.
+ */
+public interface IMenuBarCallback {
+ /**
+ * Invoked when the About menu item is selected by the user.
+ */
+ abstract public void onAboutMenuSelected();
+
+ /**
+ * Invoked when the Preferences or Options menu item is selected by the user.
+ */
+ abstract public void onPreferencesMenuSelected();
+
+ /**
+ * Used by the enhancer implementations to report errors.
+ *
+ * @param format A printf-like format string.
+ * @param args The parameters for the printf-like format string.
+ */
+ abstract public void printError(String format, Object...args);
+}
diff --git a/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java b/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java
new file mode 100644
index 0000000..d835bd6
--- /dev/null
+++ b/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 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.menubar;
+
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+
+
+/**
+ * Interface to the platform-specific MenuBarEnhancer implementation returned by
+ * {@link MenuBarEnhancer#setupMenu}.
+ */
+public interface IMenuBarEnhancer {
+
+ /** Values that indicate how the menu bar is being handlded. */
+ public enum MenuBarMode {
+ /**
+ * The Mac-specific About and Preferences are being used.
+ * No File > Exit menu should be provided by the application.
+ */
+ MAC_OS,
+ /**
+ * The provided SWT {@link Menu} is being used for About and Options.
+ * The application should provide a File > Exit menu.
+ */
+ GENERIC
+ }
+
+ /**
+ * Returns a {@link MenuBarMode} enum that indicates how the menu bar is going to
+ * or has been modified. This is implementation specific and can be called before or
+ * after {@link #setupMenu}.
+ * <p/>
+ * Callers would typically call that to know if they need to hide or display
+ * menu items. For example when {@link MenuBarMode#MAC_OS} is used, an app
+ * would typically not need to provide any "File > Exit" menu item.
+ *
+ * @return One of the {@link MenuBarMode} values.
+ */
+ public MenuBarMode getMenuBarMode();
+
+ /**
+ * Updates the menu bar to provide an About menu item and a Preferences menu item.
+ * Depending on the platform, the menu items might be decorated with the
+ * given {@code appName}.
+ * <p/>
+ * Users should not call this directly.
+ * {@link MenuBarEnhancer#setupMenu} should be used instead.
+ *
+ * @param appName Name used for the About menu item and similar. Must not be null.
+ * @param display The SWT display. Must not be null.
+ * @param callbacks Callbacks called when "About" and "Preferences" menu items are invoked.
+ * Must not be null.
+ */
+ public void setupMenu(
+ String appName,
+ Display display,
+ IMenuBarCallback callbacks);
+}
diff --git a/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java
new file mode 100644
index 0000000..eb3e817
--- /dev/null
+++ b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2011 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.menubar;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+
+
+/**
+ * On Mac, {@link MenuBarEnhancer#setupMenu} plugs a listener on the About and the
+ * Preferences menu items of the standard "application" menu in the menu bar.
+ * On Windows or Linux, it adds relevant items to a given {@link Menu} linked to
+ * the same listeners.
+ */
+public final class MenuBarEnhancer {
+
+ private MenuBarEnhancer() {
+ }
+
+ /**
+ * Creates an instance of {@link IMenuBarEnhancer} specific to the current platform
+ * and invoke its {@link IMenuBarEnhancer#setupMenu} to updates the menu bar.
+ * <p/>
+ * Depending on the platform, this will either hook into the existing About menu item
+ * and a Preferences or Options menu item or add new ones to the given {@code swtMenu}.
+ * Depending on the platform, the menu items might be decorated with the
+ * given {@code appName}.
+ * <p/>
+ * Potential errors are reported through {@link IMenuBarCallback}.
+ *
+ * @param appName Name used for the About menu item and similar. Must not be null.
+ * @param swtMenu For non-mac platform this is the menu where the "About" and
+ * the "Options" menu items are created. Typically the menu might be
+ * called "Tools". Must not be null.
+ * @param callbacks Callbacks called when "About" and "Preferences" menu items are invoked.
+ * Must not be null.
+ * @return A actual {@link IMenuBarEnhancer} implementation. Never null.
+ * This is currently not of any use for the caller but is left in case
+ * we want to expand the functionality later.
+ */
+ public static IMenuBarEnhancer setupMenu(
+ String appName,
+ final Menu swtMenu,
+ IMenuBarCallback callbacks) {
+
+ IMenuBarEnhancer enhancer = getEnhancer(callbacks);
+
+ // Default implementation for generic platforms
+ if (enhancer == null) {
+ enhancer = new IMenuBarEnhancer() {
+
+ public MenuBarMode getMenuBarMode() {
+ return MenuBarMode.GENERIC;
+ }
+
+ public void setupMenu(
+ String appName,
+ Display display,
+ final IMenuBarCallback callbacks) {
+ if (swtMenu.getItemCount() > 0) {
+ new MenuItem(swtMenu, SWT.SEPARATOR);
+ }
+
+ // Note: we use "Preferences" on Mac and "Options" on Windows/Linux.
+ final MenuItem pref = new MenuItem(swtMenu, SWT.NONE);
+ pref.setText("&Options...");
+
+ final MenuItem about = new MenuItem(swtMenu, SWT.NONE);
+ about.setText("&About...");
+
+ pref.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ try {
+ pref.setEnabled(false);
+ callbacks.onPreferencesMenuSelected();
+ super.widgetSelected(e);
+ } finally {
+ pref.setEnabled(true);
+ }
+ }
+ });
+
+ about.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ try {
+ about.setEnabled(false);
+ callbacks.onAboutMenuSelected();
+ super.widgetSelected(e);
+ } finally {
+ about.setEnabled(true);
+ }
+ }
+ });
+ }
+ };
+ }
+
+ enhancer.setupMenu(appName, swtMenu.getDisplay(), callbacks);
+ return enhancer;
+ }
+
+
+ public static IMenuBarEnhancer setupMenuManager(
+ String appName,
+ Display display,
+ final IMenuManager menuManager,
+ final IAction aboutAction,
+ final IAction preferencesAction,
+ final IAction quitAction) {
+
+ IMenuBarCallback callbacks = new IMenuBarCallback() {
+ public void printError(String format, Object... args) {
+ System.err.println(String.format(format, args));
+ }
+
+ public void onPreferencesMenuSelected() {
+ if (preferencesAction != null) {
+ preferencesAction.run();
+ }
+ }
+
+ public void onAboutMenuSelected() {
+ if (aboutAction != null) {
+ aboutAction.run();
+ }
+ }
+ };
+
+ IMenuBarEnhancer enhancer = getEnhancer(callbacks);
+
+ // Default implementation for generic platforms
+ if (enhancer == null) {
+ enhancer = new IMenuBarEnhancer() {
+
+ public MenuBarMode getMenuBarMode() {
+ return MenuBarMode.GENERIC;
+ }
+
+ public void setupMenu(
+ String appName,
+ Display display,
+ final IMenuBarCallback callbacks) {
+ if (!menuManager.isEmpty()) {
+ menuManager.add(new Separator());
+ }
+
+ if (aboutAction != null) {
+ menuManager.add(aboutAction);
+ }
+ if (preferencesAction != null) {
+ menuManager.add(preferencesAction);
+ }
+ if (quitAction != null) {
+ if (aboutAction != null || preferencesAction != null) {
+ menuManager.add(new Separator());
+ }
+ menuManager.add(quitAction);
+ }
+ }
+ };
+ }
+
+ enhancer.setupMenu(appName, display, callbacks);
+ return enhancer;
+ }
+
+ private static IMenuBarEnhancer getEnhancer(IMenuBarCallback callbacks) {
+ IMenuBarEnhancer enhancer = null;
+ String p = SWT.getPlatform();
+ String className = null;
+ if ("carbon".equals(p)) { //$NON-NLS-1$
+ className = "com.android.menubar.internal.MenuBarEnhancerCarbon"; //$NON-NLS-1$
+ } else if ("cocoa".equals(p)) { //$NON-NLS-1$
+ className = "com.android.menubar.internal.MenuBarEnhancerCocoa"; //$NON-NLS-1$
+ }
+
+ if (System.getenv("DEBUG_SWTMENUBAR") != null) {
+ callbacks.printError("DEBUG SwtMenuBar: SWT=%1$s, class=%2$s", p, className);
+ }
+
+ if (className != null) {
+ try {
+ Class<?> clazz = Class.forName(className);
+ enhancer = (IMenuBarEnhancer) clazz.newInstance();
+ } catch (Exception e) {
+ // Log an error and fallback on the default implementation.
+ callbacks.printError(
+ "Failed to instantiate %1$s: %2$s", //$NON-NLS-1$
+ className,
+ e.toString());
+ }
+ }
+ return enhancer;
+ }
+}
diff --git a/traceview/NOTICE b/traceview/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/traceview/NOTICE
@@ -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/traceview/src/com/android/traceview/MainWindow.java b/traceview/src/com/android/traceview/MainWindow.java
index 1a8e96c..b78b4f7 100644
--- a/traceview/src/com/android/traceview/MainWindow.java
+++ b/traceview/src/com/android/traceview/MainWindow.java
@@ -22,6 +22,7 @@ import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
@@ -34,6 +35,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Properties;
@@ -41,7 +43,6 @@ import java.util.Properties;
public class MainWindow extends ApplicationWindow {
private final static String PING_NAME = "Traceview";
- private final static String PING_VERSION = "1.0";
private TraceReader mReader;
private String mTraceName;
@@ -64,6 +65,13 @@ public class MainWindow extends ApplicationWindow {
protected void configureShell(Shell shell) {
super.configureShell(shell);
shell.setText("Traceview: " + mTraceName);
+
+ InputStream in = getClass().getClassLoader().getResourceAsStream(
+ "icons/traceview-128.png");
+ if (in != null) {
+ shell.setImage(new Image(shell.getDisplay(), in));
+ }
+
shell.setBounds(100, 10, 1282, 900);
}
diff --git a/traceview/src/com/android/traceview/TimeLineView.java b/traceview/src/com/android/traceview/TimeLineView.java
index 875becf..bc565dd 100644
--- a/traceview/src/com/android/traceview/TimeLineView.java
+++ b/traceview/src/com/android/traceview/TimeLineView.java
@@ -22,6 +22,7 @@ import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
@@ -284,6 +285,12 @@ public class TimeLineView extends Composite implements Observer {
}
});
+ mSurface.addMouseWheelListener(new MouseWheelListener() {
+ public void mouseScrolled(MouseEvent me) {
+ mSurface.mouseScrolled(me);
+ }
+ });
+
mTimescale.addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent me) {
@@ -842,7 +849,7 @@ public class TimeLineView extends Composite implements Observer {
}
private static enum GraphicsState {
- Normal, Marking, Scaling, Animating
+ Normal, Marking, Scaling, Animating, Scrolling
};
private class Surface extends Canvas {
@@ -966,7 +973,8 @@ public class TimeLineView extends Composite implements Observer {
int xdim = dim.x - TotalXMargin;
mScaleInfo.setNumPixels(xdim);
boolean forceEndPoints = (mGraphicsState == GraphicsState.Scaling
- || mGraphicsState == GraphicsState.Animating);
+ || mGraphicsState == GraphicsState.Animating
+ || mGraphicsState == GraphicsState.Scrolling);
mScaleInfo.computeTicks(forceEndPoints);
mCachedMinVal = mScaleInfo.getMinVal();
mCachedMaxVal = mScaleInfo.getMaxVal();
@@ -1687,6 +1695,44 @@ public class TimeLineView extends Composite implements Observer {
update();
}
+ private void mouseScrolled(MouseEvent me) {
+ mGraphicsState = GraphicsState.Scrolling;
+ double tMin = mScaleInfo.getMinVal();
+ double tMax = mScaleInfo.getMaxVal();
+ double zoomFactor = 2;
+ double tMinRef = mLimitMinVal;
+ double tMaxRef = mLimitMaxVal;
+ double t; // the fixed point
+ double tMinNew;
+ double tMaxNew;
+ if (me.count > 0) {
+ // we zoom in
+ Point dim = mSurface.getSize();
+ int x = me.x;
+ if (x < LeftMargin)
+ x = LeftMargin;
+ if (x > dim.x - RightMargin)
+ x = dim.x - RightMargin;
+ double ppr = mScaleInfo.getPixelsPerRange();
+ t = tMin + ((x - LeftMargin) / ppr);
+ tMinNew = Math.max(tMinRef, t - (t - tMin) / zoomFactor);
+ tMaxNew = Math.min(tMaxRef, t + (tMax - t) / zoomFactor);
+ } else {
+ // we zoom out
+ double factor = (tMax - tMin) / (tMaxRef - tMinRef);
+ if (factor < 1) {
+ t = (factor * tMinRef - tMin) / (factor - 1);
+ tMinNew = Math.max(tMinRef, t - zoomFactor * (t - tMin));
+ tMaxNew = Math.min(tMaxRef, t + zoomFactor * (tMax - t));
+ } else {
+ return;
+ }
+ }
+ mScaleInfo.setMinVal(tMinNew);
+ mScaleInfo.setMaxVal(tMaxNew);
+ mSurface.redraw();
+ }
+
// No defined behavior yet for double-click.
private void mouseDoubleClick(MouseEvent me) {
}
diff --git a/traceview/src/resources/icons/traceview-128.png b/traceview/src/resources/icons/traceview-128.png
new file mode 100644
index 0000000..5b4eff1
--- /dev/null
+++ b/traceview/src/resources/icons/traceview-128.png
Binary files differ